diff --git a/README.md b/README.md
index d700d1b..021714c 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,8 @@
# EmoCheck
+[![GitHub release](https://img.shields.io/github/release/jpcertcc/emocheck.svg)](https://github.com/jpcertcc/emocheck/releases)
+[![Github All Releases](https://img.shields.io/github/downloads/jpcertcc/emocheck/total.svg)](http://www.somsubhra.com/github-release-stats/?username=jpcertcc&repository=emocheck)
+
Emotet detection tool for Windows OS.
## How to use
@@ -10,48 +13,104 @@ Emotet detection tool for Windows OS.
## Download
-Please download from [Releases](https://github.com/JPCERTCC/EmoCheck/releases) page.
+Please download from the [Releases](https://github.com/JPCERTCC/EmoCheck/releases) page.
+
+Latest hash:
> emocheck_x86.exe
-> MD5 : 9508DACDF443B422D159160E02043045
-> SHA256: 3F9BEFD9287923A844FA7F38DEADFE2238380398E1F4F4C902A18CD4CCF1BFA0
+> MD5 : 89863A79D531E2730D450F2D1C99EB6C
+> SHA256: 5A459538DE0A5B1C270C0617191A71D23EA6C705650761EF9B7095A736AF7301
> emocheck_x64.exe
-> MD5 : 9E1B8BE8402A51B8FEE0B590B4965060
-> SHA256: C8CC438BF271DFAA110C58C748C54175823269DA7202EA19FC75EEEA359FAEB5
+> MD5 : 94005A6447CA810619FF24D67EF67A93
+> SHA256: 65838C35D03FE36E9DBA1408E2278F8BC282B1319FEFAABEE4491B45E1254163
+
+## Command options
+
+(since v0.0.2)
+
+- Specify output directory for the report (default: current directory)
+ - `/output [your output directory]` or `-output [your output directory]`
+- No console output
+ - `/quiet` or `-quiet`
+- Export the report in JSON style
+ - `/json` or `-json`
+- Debug mode (no report)
+ - `/debug` or `-debug`
+- Show help
+ - `/help` or `-help`
## How EmoCheck detects Emotet
-Emotet generates their process name from a specific word dictionary and C drive serial.
+(v0.0.1)
+Emotet generates their process name from a specific word dictionary and C drive serial number.
EmoCheck scans the running process on the host, and find Emotet process from their process name.
+(added in v0.0.2)
+Emotet keeps their encoded process name in a specific registry key.
+EmoCheck looks up and decode the registry value, and find it from the process list.
+
## Sample Report
+Text stlye:
+
```txt
-[Emocheck v0.0.1]
-Scan time: 2020-02-03 13:06:20
+[Emocheck v0.0.2]
+Scan time: 2020-02-10 13:06:20
____________________________________________________
-[Result]
+[Result]
Detected Emotet process.
-[Emotet Process]
- Process Name : khmerbid.exe
- Process ID : 10508
- Image Path : C:\Users\[username]\AppData\Local\khmerbid.exe
+[Emotet Process]
+ Process Name : mstask.exe
+ Process ID : 716
+ Image Path : C:\Users\[username]\AppData\Local\mstask.exe
____________________________________________________
Please remove or isolate the suspicious execution file.
```
+JSON style (added in v0.0.2):
+
+```json
+{
+ "scan_time":"2020-02-10 13:06:20",
+ "hostname":"[your hostname]",
+ "emocheck_version":"0.0.2",
+ "is_infected":"yes",
+ "emotet_processes":[
+ {
+ "process_name":"mstask.exe",
+ "process_id":"716",
+ "image_path":"C:\\Users\\[username]\\AppData\\Local\\mstask.exe"
+ }
+ ]
+}
+```
+
The report will be exported to the following path.
-- [path of emocheck.exe]\yyyymmddhhmmss_emocheck.txt
+(v0.0.1)
+`[current directory]\yyyymmddhhmmss_emocheck.txt`
+
+(since v0.0.2)
+`[output path]\[computer name]_yyyymmddhhmmss_emocheck.txt`
+`[output path]\[computer name]_yyyymmddhhmmss_emocheck.json`
## Screenshot
+(v0.0.1)
+## Releases
+
+- (Feb. 3, 2020) v0.0.1
+ - Initial release
+- (Feb. 10, 2020) v0.0.2
+ - update detecting method
+ - add options
+
## Notes
### Tested environments
diff --git a/README_jp.md b/README_jp.md
index dee87d2..f685f8e 100644
--- a/README_jp.md
+++ b/README_jp.md
@@ -1,62 +1,115 @@
# EmoCheck
-Windows OS 向け Emotet 検知ツール
+[![GitHub release](https://img.shields.io/github/release/jpcertcc/emocheck.svg)](https://github.com/jpcertcc/emocheck/releases)
+[![Github All Releases](https://img.shields.io/github/downloads/jpcertcc/emocheck/total.svg)](http://www.somsubhra.com/github-release-stats/?username=jpcertcc&repository=emocheck)
+
+Windows OS 用 Emotet 検知ツール
## 使用方法
1. Releases からツールをダウンロード
2. 感染が疑われるホストでツールを実行
-3. 出力されるレポートテキストを確認
+3. 出力されるレポートを確認
## ダウンロード
以下のページからダウンロードできます。
- [Release](https://github.com/JPCERTCC/EmoCheck/releases)
+ [Releases](https://github.com/JPCERTCC/EmoCheck/releases)
なお、ファイルのハッシュ値は以下の通りです。
> emocheck_x86.exe
-> MD5 : 9508DACDF443B422D159160E02043045
-> SHA256: 3F9BEFD9287923A844FA7F38DEADFE2238380398E1F4F4C902A18CD4CCF1BFA0
+> MD5 : 89863A79D531E2730D450F2D1C99EB6C
+> SHA256: 5A459538DE0A5B1C270C0617191A71D23EA6C705650761EF9B7095A736AF7301
> emocheck_x64.exe
-> MD5 : 9E1B8BE8402A51B8FEE0B590B4965060
-> SHA256: C8CC438BF271DFAA110C58C748C54175823269DA7202EA19FC75EEEA359FAEB5
+> MD5 : 94005A6447CA810619FF24D67EF67A93
+> SHA256: 65838C35D03FE36E9DBA1408E2278F8BC282B1319FEFAABEE4491B45E1254163
+
+## コマンドオプション
+
+(v0.0.2 追加)
+
+- レポート出力先ディレクトリ指定 (デフォルト: カレントディレクトリ)
+ - `/output [出力先ディレクトリ]` または `-output [出力先ディレクトリ]`
+- コマンドライン出力抑止
+ - `/quiet` または `-quiet`
+- JSON形式でのレポート出力
+ - `/json` または `-json`
+- 詳細表示(レポート出力なし)
+ - `/debug` または `-debug`
+- ヘルプ表示
+ - `/help` または `-help`
## Emotetの検知方法
-EmoCheck ではホストのプロセス名から Emotet のプロセスを検知します。
+EmoCheck はホスト上のプロセス一覧から Emotet のプロセスを検知します。
## レポート例
-Emotetが検知された場合、以下のようなレポートが作成されます。
+Emotetが検知された場合、以下のようなレポートが作成されます。
+
+テキスト形式:
```txt
-[Emocheck v0.0.1]
-プログラム実行時刻: 2020-02-03 13:45:51
+[Emocheck v0.0.2]
+プログラム実行時刻: 2020-02-10 10:45:51
____________________________________________________
[結果]
Emotetを検知しました
[詳細]
- プロセス名 : khmerbid.exe
- プロセスID : 6132
- イメージパス : C:\Users\[username]\AppData\Local\khmerbid.exe
+ プロセス名 : mstask.exe
+ プロセスID : 716
+ イメージパス : C:\Users\[ユーザー名]\AppData\Local\mstask.exe
____________________________________________________
-イメージパスの実行ファイルを隔離/削除してください。
+不審なイメージパスの実行ファイルを隔離/削除してください。
+```
+
+JSON形式 (v0.0.2 追加):
+
+```json
+{
+ "scan_time":"2020-02-10 10:45:51",
+ "hostname":"[ホスト名]",
+ "emocheck_version":"0.0.2",
+ "is_infected":"yes",
+ "emotet_processes":[
+ {
+ "process_name":"mstask.exe",
+ "process_id":"716",
+ "image_path":"C:\\Users\\[ユーザー名]\\AppData\\Local\\mstask.exe"
+ }
+ ]
+}
```
レポートは以下のパスに生成されます。
-- [emocheck.exe を実行したフォルダー]\yyyymmddhhmmss_emocheck.txt
+(v0.0.1)
+`[カレントディレクトリ]\yyyymmddhhmmss_emocheck.txt`
+
+(v0.0.2 以降)
+`[指定ディレクトリ]\[ホスト名]_yyyymmddhhmmss_emocheck.txt`
+`[指定ディレクトリ]\[ホスト名]_yyyymmddhhmmss_emocheck.json`
+
+Emotetが検知された場合、以下のようなレポートが作成されます。
## スクリーンショット
+(v0.0.1)
+## 更新履歴
+
+- (2020/02/03) v0.0.1
+- (2020/02/10) v0.0.2
+ - 検知手法の追加
+ - コマンドオプションの追加
+
## その他
### 動作確認環境
diff --git a/emocheck/emocheck.cpp b/emocheck/emocheck.cpp
index bda32da..05c0971 100644
--- a/emocheck/emocheck.cpp
+++ b/emocheck/emocheck.cpp
@@ -1,4 +1,4 @@
-/**
+/*
* LICENSE
* Please reffer to the LICENSE.txt in the https://github.com/JPCERTCC/EmoCheck/
*/
@@ -6,7 +6,9 @@
#include "emocheck.h"
// standard modules
+#include
#include
+#include
#include
// windows basic modules
@@ -18,6 +20,8 @@
namespace emocheck {
+bool is_debug = false;
+
std::string WideCharToString(wchar_t *wide_char) {
int buf_size = WideCharToMultiByte(CP_OEMCP, 0, wide_char, -1, (char *)NULL, 0, NULL, NULL);
char *char_ptr = new char[buf_size];
@@ -26,7 +30,122 @@ std::string WideCharToString(wchar_t *wide_char) {
std::string result(char_ptr, char_ptr + buf_size - 1);
delete[] char_ptr;
- return (result);
+ return result;
+}
+
+std::vector IntToBytes(unsigned int integer) {
+ std::vector bytes_vector(4);
+ for (int i = 0; i < 4; i++)
+ bytes_vector[i] = (integer >> (i * 8));
+ return bytes_vector;
+}
+
+std::string QueryRegistry(unsigned int serial, HKEY root, std::wstring key_path) {
+ DWORD buf_len;
+ LSTATUS status;
+ HKEY hKey;
+ std::wstringstream wstring_serial;
+ std::string filename = "";
+
+ // convert integer to wstring stream
+ wstring_serial << std::hex << serial;
+
+ status = RegOpenKeyEx(root, key_path.c_str(), 0, KEY_READ, &hKey);
+ if (status) {
+ // std::cerr << "[*] Emotet registry key not found. status: " << status << std::endl;
+ return filename;
+ }
+
+ // debug message
+ if (is_debug) {
+ if (root == HKEY_CURRENT_USER) {
+ std::wcout << L"[DEBUG] Open registry: HKCU\\" << key_path << std::endl;
+ } else if (root == HKEY_LOCAL_MACHINE) {
+ std::wcout << L"[DEBUG] Open registry: HKLM\\" << key_path << std::endl;
+ } else {
+ std::wcout << L"[DEBUG] Open registry: " << std::hex << root << L" path: " << key_path << std::endl;
+ }
+ }
+
+ status = RegGetValue(hKey, nullptr, wstring_serial.str().c_str(), RRF_RT_REG_BINARY, nullptr, nullptr, &buf_len);
+ if (status) {
+ // std::cerr << "[*] Emotet registry key not found. status: " << status << std::endl;
+ return filename;
+ }
+
+ // debug message
+ if (is_debug) {
+ std::wcout << L"[DEBUG] Found suspicous subkey: " << wstring_serial.str() << std::endl;
+ std::cout << "[DEBUG] Data length: " << buf_len << std::endl;
+ }
+
+ // buffur to save binary_data in registry value.
+ unsigned char *buffer = new unsigned char[buf_len];
+
+ RegGetValue(hKey, nullptr, wstring_serial.str().c_str(), RRF_RT_REG_BINARY, nullptr, buffer, &buf_len);
+
+ // XOR registry value with drive serial num;
+ unsigned char *decoded_chars = new unsigned char[buf_len];
+ std::vector xor_key = IntToBytes(serial);
+
+ // debug message
+ if (is_debug) {
+ std::cout << "[DEBUG] Binary data:\n[DEBUG] ";
+ for (unsigned int i = 0; i < buf_len; i++) {
+ printf("0x%02x ", buffer[i]);
+ }
+ std::cout << std::endl;
+ }
+
+ for (unsigned int i = 0; i < buf_len; i++) {
+ decoded_chars[i] = (xor_key[i % 4] ^ buffer[i]);
+ }
+
+ // debug message
+ if (is_debug) {
+ std::cout << "[DEBUG] Decoded data:\n[DEBUG] ";
+ for (unsigned int i = 0; i < buf_len; i++) {
+ printf("0x%02x ", decoded_chars[i]);
+ }
+ std::cout << std::endl;
+ }
+
+ for (unsigned int i = 0; i < buf_len; i++) {
+ if (0x20 < int(decoded_chars[i]) && int(decoded_chars[i]) < 0x7e) {
+ filename += decoded_chars[i];
+ }
+ }
+ // debug message
+ if (is_debug) {
+ std::cout << "[DEBUG] Decoded string: " << filename << std::endl;
+ }
+ return filename;
+}
+
+std::vector GetEmotetFileNameFromRegistry(int serial) {
+ std::vector filenames;
+ std::string filename;
+ std::wstring reg_key_path = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer";
+ std::wstring reg_key_path_admin = L"Software\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Explorer";
+
+ // if emotet runs with user auth.(x64,x32)
+ filename = QueryRegistry(serial, HKEY_CURRENT_USER, reg_key_path);
+ if (filename.length() > 0) {
+ filenames.push_back(filename);
+ }
+
+ // if emotet runs with admin auth. (x32)
+ filename = QueryRegistry(serial, HKEY_LOCAL_MACHINE, reg_key_path);
+ if (filename.length() > 0) {
+ filenames.push_back(filename);
+ }
+
+ // if emotet runs with admin auth. (x64)
+ filename = QueryRegistry(serial, HKEY_LOCAL_MACHINE, reg_key_path_admin);
+ if (filename.length() > 0) {
+ filenames.push_back(filename);
+ }
+ return filenames;
}
std::string ConvertDivecePath(wchar_t *imagepath) {
@@ -55,25 +174,37 @@ std::string GetImageFileName(DWORD pid) {
wchar_t imagepath[MAX_PATH + 1];
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (hProcess == NULL) {
- std::clog << "[!!] Error: Fail to get handle from pid " << pid << std::endl;
- std::exit(1);
+ std::clog << "[!!] Error: Failed to get handle from pid " << pid << std::endl;
+ return std::string("");
}
if (GetProcessImageFileName(hProcess, imagepath, sizeof(imagepath) / sizeof(*imagepath)) == 0) {
- std::clog << "[!!] Error: Fail to get image file name from pid " << pid << std::endl;
- std::exit(1);
+ std::clog << "[!!] Error: Failed to get image file name from pid " << pid << std::endl;
+ return std::string("");
} else {
return ConvertDivecePath(imagepath);
}
}
-int GetVolumeSerialNumber() {
+unsigned int GetVolumeSerialNumber() {
+ wchar_t windows_directory_path[MAX_PATH + 1] = {0};
wchar_t volumename[MAX_PATH + 1] = {0};
wchar_t filesystemname[MAX_PATH + 1] = {0};
+ std::wstring drive_letter;
DWORD serialnumber = 0;
DWORD max_componentlen = 0;
DWORD filesystem_flags = 0;
+
+ // get drive letter from system drive
+ if (GetWindowsDirectory(windows_directory_path, MAX_PATH)) {
+ for (int i = 0; i < 3; i++) {
+ drive_letter += windows_directory_path[i];
+ }
+ } else {
+ drive_letter = std::wstring(L"C:\\");
+ }
+
if (GetVolumeInformation(
- L"C:\\",
+ drive_letter.c_str(),
volumename,
sizeof(volumename),
&serialnumber,
@@ -81,7 +212,6 @@ int GetVolumeSerialNumber() {
&filesystem_flags,
filesystemname,
sizeof(filesystemname)) == TRUE) {
- // std::cout << "[debug] Serial Number :" << std::hex << serialnumber << std::endl;
} else {
std::clog << "[!] GetVolumeInformation() failed, error " << GetLastError() << std::endl;
std::exit(1);
@@ -122,6 +252,8 @@ std::string GenerateEmotetProcessName() {
std::string keywords;
std::string keyword;
+ seed = GetVolumeSerialNumber();
+
keywords =
"duck,mfidl,targets,ptr,khmer,purge,metrics,acc,inet,msra,symbol,driver,"
"sidebar,restore,msg,volume,cards,shext,query,roam,etw,mexico,basic,url,"
@@ -132,7 +264,6 @@ std::string GenerateEmotetProcessName() {
keylen = int(keywords.length());
// first round
- seed = GetVolumeSerialNumber();
q = seed / keylen;
mod = seed % keylen;
keyword += GetWord(keywords, mod, keylen);
@@ -142,11 +273,10 @@ std::string GenerateEmotetProcessName() {
mod = seed % keylen;
keyword += GetWord(keywords, mod, keylen);
- // std::cout << "[debug] Emotet process name on this host is " << keyword.c_str() << ".exe" << std::endl;
return keyword;
}
-std::vector ScanEmotetProcess(std::string keyword) {
+std::vector ScanEmotetProcess(std::vector keywords) {
PROCESSENTRY32 pe = {sizeof(PROCESSENTRY32)};
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
BOOL result;
@@ -155,24 +285,52 @@ std::vector ScanEmotetProcess(std::string keyword) {
for (result = Process32First(snapshot, &pe);
result == TRUE; result = Process32Next(snapshot, &pe)) {
std::string process_name = WideCharToString(pe.szExeFile);
- if (process_name.find(keyword) != std::string::npos) {
- EmotetProcess emotet_process;
- emotet_process.pid = pe.th32ProcessID;
- emotet_process.process_name = process_name;
- emotet_process.image_path = GetImageFileName(emotet_process.pid);
- emotet_processes.push_back(emotet_process);
+ for (unsigned int i = 0; i < keywords.size(); i++) {
+ if (process_name == keywords[i] + std::string(".exe")) {
+ EmotetProcess emotet_process;
+ emotet_process.pid = int(pe.th32ProcessID);
+ emotet_process.process_name = process_name;
+ emotet_process.image_path = GetImageFileName(emotet_process.pid);
+ emotet_processes.push_back(emotet_process);
+ }
}
}
return emotet_processes;
}
-std::vector ScanEmotet() {
+std::vector ScanEmotet(bool debug) {
+ std::vector filenames;
std::string emotet_process_name;
+ std::vector emotet_process_names;
std::vector emotet_processes;
+ is_debug = debug;
+
+ // old emotet (- 2020/02/05)
emotet_process_name = GenerateEmotetProcessName();
- emotet_processes = ScanEmotetProcess(emotet_process_name);
+ emotet_process_names.push_back(emotet_process_name);
+
+ // new emotet (2020/02/06 -)
+ filenames = GetEmotetFileNameFromRegistry(GetVolumeSerialNumber());
+ for (unsigned int i = 0; i < filenames.size(); i++) {
+ if (filenames[i].length() != 0) {
+ emotet_process_names.push_back(filenames[i]);
+ }
+ }
+ // unique emotet processes names
+ std::sort(emotet_process_names.begin(), emotet_process_names.end());
+ emotet_process_names.erase(std::unique(emotet_process_names.begin(), emotet_process_names.end()), emotet_process_names.end());
+
+ // debug message
+ if (is_debug) {
+ std::cout << "[DEBUG] Search following name(s) from running processes: ";
+ for (unsigned int i = 0; i < emotet_process_names.size(); i++) {
+ std::cout << emotet_process_names[i] << " ";
+ }
+ std::cout << std::endl;
+ }
+ emotet_processes = ScanEmotetProcess(emotet_process_names);
return emotet_processes;
}
diff --git a/emocheck/emocheck.h b/emocheck/emocheck.h
index 731c299..acd0859 100644
--- a/emocheck/emocheck.h
+++ b/emocheck/emocheck.h
@@ -1,4 +1,4 @@
-/**
+/*
* LICENSE
* Please reffer to the LICENSE.txt in the https://github.com/JPCERTCC/EmoCheck/
*/
@@ -11,8 +11,8 @@
namespace emocheck {
-static char EMOCHECK_VERSION[] = "0.0.1";
-static char EMOCHECK_RELEASE_DATE[] = "2020/02/03";
+static char EMOCHECK_VERSION[] = "0.0.2";
+static char EMOCHECK_RELEASE_DATE[] = "2020/02/10";
static char EMOCHECK_URL[] = "https://github.com/JPCERTCC/EmoCheck";
static char LINE_DELIMITER[] =
"____________________________________________________\n";
@@ -25,7 +25,8 @@ struct EmotetProcess {
std::string image_path;
};
-std::vector ScanEmotet();
+std::string WideCharToString(wchar_t *wide_char);
+std::vector ScanEmotet(bool);
} //namespace emocheck
diff --git a/emocheck/emocheck.vcxproj b/emocheck/emocheck.vcxproj
index a55ced2..fa1b4be 100644
--- a/emocheck/emocheck.vcxproj
+++ b/emocheck/emocheck.vcxproj
@@ -72,9 +72,11 @@
true
+ false
true
+ false
false
@@ -82,36 +84,79 @@
false
+ false
- Use
- Level3
+ NotUsing
+ Level4
Disabled
true
WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- pch.h
+
+
+ MultiThreaded
+
+
+ stdcpp17
+ /utf-8 %(AdditionalOptions)
Console
- true
+ false
+
+
+
+
+ false
+
+
+
+
+
+ false
+
+
+
+
- Use
- Level3
+ NotUsing
+ Level4
Disabled
true
_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- pch.h
+
+
+ MultiThreaded
+ stdcpp17
+
+
+ /utf-8 %(AdditionalOptions)
Console
- true
+ false
+
+
+
+
+ false
+
+
+
+
+
+ false
+
+
+
+
@@ -123,34 +168,72 @@
true
WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- pch.h
+
+
+ MultiThreaded
+
+
+ stdcpp17
+ /utf-8 %(AdditionalOptions)
Console
true
true
false
- Default
+
+
+
+
+ false
+
+
+
+
+
+ false
+
+
+
+
- Use
- Level3
+ NotUsing
+ Level4
MaxSpeed
true
true
true
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- pch.h
+
+
+ MultiThreaded
+
+
+ stdcpp17
+ /utf-8 %(AdditionalOptions)
Console
true
true
- true
+ false
+
+
+
+
+ false
+
+
+
+
+
+
+
diff --git a/emocheck/main.cpp b/emocheck/main.cpp
index 0451a00..20f74f2 100644
--- a/emocheck/main.cpp
+++ b/emocheck/main.cpp
@@ -1,4 +1,4 @@
-/**
+/*
* LICENSE
* Please reffer to the LICENSE.txt in the https://github.com/JPCERTCC/EmoCheck/
*/
@@ -7,19 +7,38 @@
#include "emocheck.h"
// standard modules
+#include
#include
-#include
#include
#include
// windows basic module
#include
-namespace emocheck
-{
+// defines
+#define PARAM_SWITCH1 '/'
+#define PARAM_SWITCH2 '-'
+#define PARAM_QUIET "quiet"
+#define PARAM_DEBUG "debug"
+#define PARAM_OUTPUT "output"
+#define PARAM_HELP "help"
+#define PARAM_JSON "json"
-void PrintBanner()
-{
+namespace emocheck {
+
+bool is_param(const char *str) {
+ if (!str) return false;
+
+ const size_t len = strlen(str);
+ if (len < 2) return false;
+
+ if (str[0] == PARAM_SWITCH1 || str[0] == PARAM_SWITCH2) {
+ return true;
+ }
+ return false;
+}
+
+void PrintBanner() {
char banner[] =
" ______ _____ _ _ \n"
"| ____| / ____| | | | \n"
@@ -40,53 +59,59 @@ void PrintBanner()
// unsigned short int usrDefLangId = GetUserDefaultLangID();
}
-void PrintReport(std::vector emotet_processes)
-{
- if (GetUserDefaultLangID() == LANG_ID_JP)
- {
+void PrintHelp() {
+ if (GetUserDefaultLangID() == LANG_ID_JP) {
+ // Japanese help
+ std::cout << "[オプション説明]\n"
+ << "コマンドラインの出力抑止:\n\t /quiet または -quiet\n"
+ << "JSON形式でのレポート出力:\n\t /json または -json\n"
+ << "レポート出力先ディレクトリ指定 (デフォルト カレントディレクトリ):\n\t /output [出力先ディレクトリ] または -output [出力先ディレクトリ]\n"
+ << "詳細表示:\n\t/debug または -debug" << std::endl;
+ } else {
+ // English Help
+ std::cout << "[Options]\n"
+ << "Suppress command line output:\n\t/quiet or -quiet\n"
+ << "Export report in JSON sytle:\n\t/json or -json\n"
+ << "Set output directory (default: current directory ):\n\t/output [output directory] or -output [output directory]\n"
+ << "Debug mode:\n\t/debug or -debug" << std::endl;
+ }
+}
+
+void PrintReport(std::vector emotet_processes) {
+ if (GetUserDefaultLangID() == LANG_ID_JP) {
// Japanese Report
- if (emotet_processes.size() > 0)
- {
+ if (emotet_processes.size() > 0) {
std::cout.imbue(std::locale(""));
- for (int i = 0; i < emotet_processes.size(); ++i)
- {
+ for (unsigned int i = 0; i < emotet_processes.size(); ++i) {
std::cout << "[!!] Emotet 検知"
<< "\n"
- << " プロセス名 : " << emotet_processes[i].process_name.c_str() << "\n"
+ << " プロセス名 : " << emotet_processes[i].process_name << "\n"
<< " プロセスID : " << emotet_processes[i].pid << "\n"
- << " イメージパス : " << emotet_processes[i].image_path.c_str() << std::endl;
+ << " イメージパス : " << emotet_processes[i].image_path << std::endl;
}
std::cout << LINE_DELIMITER << std::endl;
std::cout << "Emotetのプロセスが見つかりました。\n"
- << "イメージパスの実行ファイルを隔離/削除してください。\n"
+ << "不審なイメージパスの実行ファイルを隔離/削除してください。\n"
<< std::endl;
- }
- else
- {
+ } else {
std::cout << "Emotetは検知されませんでした。\n"
<< std::endl;
}
- }
- else
- {
+ } else {
// English Report
- if (emotet_processes.size() > 0)
- {
- for (int i = 0; i < emotet_processes.size(); ++i)
- {
+ if (emotet_processes.size() > 0) {
+ for (unsigned int i = 0; i < emotet_processes.size(); ++i) {
std::cout << "[!!] Detected"
<< "\n"
- << " Process Name: " << emotet_processes[i].process_name.c_str() << "\n"
+ << " Process Name: " << emotet_processes[i].process_name << "\n"
<< " PID : " << emotet_processes[i].pid << "\n"
- << " Image Path : " << emotet_processes[i].image_path.c_str() << std::endl;
+ << " Image Path : " << emotet_processes[i].image_path << std::endl;
}
std::cout << LINE_DELIMITER << std::endl;
- std::cout << "Emotet had be detected.\n"
+ std::cout << "Emotet had been detected.\n"
<< "Please remove or isolate the suspicious execution file.\n"
<< std::endl;
- }
- else
- {
+ } else {
std::cout << "No detection.\n"
<< std::endl;
}
@@ -94,100 +119,249 @@ void PrintReport(std::vector emotet_processes)
return;
}
-void WriteReport(std::vector emotet_processes)
-{
- char filename[28];
+void WriteReport(std::vector emotet_processes, bool is_quiet, std::string output_path) {
+ std::string filename;
+ char time_file[16];
char time_iso8601[20];
+ wchar_t computer_name[256] = {'\0'};
+ unsigned long dword_size = sizeof(computer_name) / sizeof(computer_name[0]);
+ std::string hostname;
+
+ if (GetComputerName(computer_name, &dword_size)) {
+ hostname = emocheck::WideCharToString(computer_name);
+ } else {
+ hostname = std::string("");
+ }
time_t t = time(nullptr);
struct tm local_time;
localtime_s(&local_time, &t);
std::strftime(time_iso8601, 20, "%Y-%m-%d %H:%M:%S", &local_time);
- std::strftime(filename, 28, "%Y%m%d%H%M%S_emocheck.txt", &local_time);
+ std::strftime(time_file, 16, "%Y%m%d%H%M%S", &local_time);
- std::ofstream outputfile(filename);
+ filename += output_path;
+ filename += "\\";
+ filename += std::string(hostname);
+ filename += std::string("_");
+ filename += std::string(time_file);
+ filename += std::string("_emocheck.txt");
- if (GetUserDefaultLangID() == LANG_ID_JP)
- {
+ std::ofstream outputfile(filename.c_str());
+
+ if (GetUserDefaultLangID() == LANG_ID_JP) {
// Japanese Report
- outputfile << "[Emocheck v" << EMOCHECK_VERSION << "]" << std::endl;
+ outputfile << "[EmoCheck v" << EMOCHECK_VERSION << "]" << std::endl;
outputfile << "プログラム実行時刻: " << time_iso8601 << std::endl;
outputfile << LINE_DELIMITER << std::endl;
- if (emotet_processes.size() > 0)
- {
+ if (emotet_processes.size() > 0) {
outputfile << "[結果]\n"
<< "Emotetを検知しました。\n"
<< std::endl;
- for (int i = 0; i < emotet_processes.size(); ++i)
- {
+ for (unsigned int i = 0; i < emotet_processes.size(); ++i) {
outputfile << "[詳細]\n"
- << " プロセス名 : " << emotet_processes[i].process_name.c_str() << "\n"
+ << " プロセス名 : " << emotet_processes[i].process_name << "\n"
<< " プロセスID : " << emotet_processes[i].pid << "\n"
- << " イメージパス : " << emotet_processes[i].image_path.c_str() << std::endl;
+ << " イメージパス : " << emotet_processes[i].image_path << std::endl;
}
outputfile << LINE_DELIMITER << std::endl;
outputfile << "イメージパスの実行ファイルを隔離/削除してください。" << std::endl;
- }
- else
- {
+ } else {
outputfile << "[結果]\n"
<< "検知しませんでした。" << std::endl;
}
outputfile.close();
- std::cout.imbue(std::locale(""));
- std::cout << "以下のファイルに結果を出力しました。" << std::endl;
- std::cout << "\n\t" << filename << "\n"
- << std::endl;
- std::cout << "ツールのご利用ありがとうございました。\n"
- << std::endl;
- }
- else
- {
+ if (!is_quiet) {
+ std::cout.imbue(std::locale(""));
+ std::cout << "以下のファイルに結果を出力しました。" << std::endl;
+ std::cout << "\n\t" << filename << "\n"
+ << std::endl;
+ std::cout << "ツールのご利用ありがとうございました。\n"
+ << std::endl;
+ }
+ } else {
// English Report
- outputfile << "[Emocheck v" << EMOCHECK_VERSION << "]" << std::endl;
+ outputfile << "[EmoCheck v" << EMOCHECK_VERSION << "]" << std::endl;
outputfile << "Scan time: " << time_iso8601 << std::endl;
outputfile << LINE_DELIMITER << std::endl;
- if (emotet_processes.size() > 0)
- {
+ if (emotet_processes.size() > 0) {
outputfile << "[Result] \nDetected Emotet process.\n"
<< std::endl;
- for (int i = 0; i < emotet_processes.size(); ++i)
- {
+ for (unsigned int i = 0; i < emotet_processes.size(); ++i) {
outputfile << "[Emotet Process] \n"
- << " Process Name : " << emotet_processes[i].process_name.c_str() << "\n"
+ << " Process Name : " << emotet_processes[i].process_name << "\n"
<< " Process ID : " << emotet_processes[i].pid << "\n"
- << " Image Path : " << emotet_processes[i].image_path.c_str() << std::endl;
+ << " Image Path : " << emotet_processes[i].image_path << std::endl;
}
outputfile << LINE_DELIMITER << std::endl;
outputfile << "Please remove or isolate the suspicious execution file." << std::endl;
- }
- else
- {
+ } else {
outputfile << "[Result] \nNo detection." << std::endl;
}
outputfile.close();
- std::cout << "Report has exported to following file." << std::endl;
- std::cout << "\n\t" << filename << "\n"
- << std::endl;
- std::cout << "Thank you for using our tool.\n"
- << std::endl;
+ if (!is_quiet) {
+ std::cout << "Report has exported to following file." << std::endl;
+ std::cout << "\n\t" << filename << "\n"
+ << std::endl;
+ std::cout << "Thank you for using our tool.\n"
+ << std::endl;
+ }
}
return;
}
-} // namespace emocheck
+std::string EscapeBackSlash(std::string s) {
+ std::string target = "\\";
+ std::string replacement = "\\\\";
+ if (!target.empty()) {
+ std::string::size_type pos = 0;
+ while ((pos = s.find(target, pos)) != std::string::npos) {
+ s.replace(pos, target.length(), replacement);
+ pos += replacement.length();
+ }
+ }
+ return s;
+}
+
+void JsonReport(std::vector emotet_processes, bool is_quiet, std::string output_path) {
+ std::string filename;
+ char time_file[16];
+ char time_iso8601[20];
+ wchar_t computer_name[256] = {'\0'};
+ unsigned long dword_size = sizeof(computer_name) / sizeof(computer_name[0]);
+ std::string hostname;
+
+ if (GetComputerName(computer_name, &dword_size)) {
+ hostname = emocheck::WideCharToString(computer_name);
+ } else {
+ hostname = std::string("");
+ }
-int main()
-{
+ time_t t = time(nullptr);
+ struct tm local_time;
+
+ localtime_s(&local_time, &t);
+ std::strftime(time_iso8601, 20, "%Y-%m-%d %H:%M:%S", &local_time);
+ std::strftime(time_file, 16, "%Y%m%d%H%M%S", &local_time);
+
+ filename += output_path;
+ filename += "\\";
+ filename += std::string(hostname);
+ filename += std::string("_");
+ filename += std::string(time_file);
+ filename += std::string("_emocheck.json");
+
+ std::ofstream outputfile(filename.c_str());
+
+ outputfile << "{\n \"scan_time\":\"" << time_iso8601 << "\",\n"
+ << " \"hostname\":\"" << hostname << "\",\n"
+ << " \"emocheck_version\":\"" << EMOCHECK_VERSION << "\"," << std::endl;
+ if (emotet_processes.size() > 0) {
+ outputfile << " \"is_infected\":\"yes\",\n \"emotet_processes\":[" << std::endl;
+ for (unsigned int i = 0; i < emotet_processes.size(); ++i) {
+ outputfile << " {\n"
+ << " \"process_name\":\"" << emotet_processes[i].process_name << "\",\n"
+ << " \"process_id\":\"" << emotet_processes[i].pid << "\",\n"
+ << " \"image_path\":\"" << EscapeBackSlash(emotet_processes[i].image_path) << "\"" << std::endl;
+ if (i == emotet_processes.size() - 1) {
+ outputfile << " }" << std::endl;
+ } else {
+ outputfile << " }," << std::endl;
+ }
+ }
+ outputfile << " ]\n}" << std::endl;
+ } else {
+ outputfile << " \"is_infected\":\"no\"\n}" << std::endl;
+ }
+ outputfile.close();
+ if (!is_quiet) {
+ if (GetUserDefaultLangID() == LANG_ID_JP) {
+ std::cout.imbue(std::locale(""));
+ std::cout << "以下のファイルに結果を出力しました。" << std::endl;
+ std::cout << "\n\t" << filename << "\n"
+ << std::endl;
+ std::cout << "ツールのご利用ありがとうございました。\n"
+ << std::endl;
+ } else {
+ std::cout << "Report has exported to following file." << std::endl;
+ std::cout << "\n\t" << filename << "\n"
+ << std::endl;
+ std::cout << "Thank you for using our tool.\n"
+ << std::endl;
+ }
+ }
+ return;
+}
+
+} // namespace emocheck
+
+int main(int argc, char *argv[]) {
std::vector scan_result;
+ bool is_debug = false;
+ bool is_quiet = false;
+ bool is_json = false;
+ std::string output_path = ".";
- emocheck::PrintBanner();
- scan_result = emocheck::ScanEmotet();
- emocheck::PrintReport(scan_result);
- emocheck::WriteReport(scan_result);
+ if (argc < 2) {
+ emocheck::PrintBanner();
+ scan_result = emocheck::ScanEmotet(is_debug);
+ emocheck::PrintReport(scan_result);
+ emocheck::WriteReport(scan_result, is_quiet, output_path);
+ system("pause");
+ return 0;
+ }
- system("pause");
+ // Parse parameters
+ for (int i = 1; i < argc; i++) {
+ if (emocheck::is_param(argv[i])) {
+ const char *param = &argv[i][1];
+ if (!strcmp(param, PARAM_QUIET)) {
+ is_quiet = true;
+ } else if (!strcmp(param, PARAM_DEBUG)) {
+ is_debug = true;
+ } else if (!strcmp(param, PARAM_JSON)) {
+ is_json = true;
+ } else if (!strcmp(param, PARAM_OUTPUT)) {
+ const char *next_param = &argv[i + 1][0];
+ if (std::filesystem::exists(std::string(next_param))) {
+ output_path = std::string(next_param);
+ i++;
+ } else {
+ std::cout << "Invalid output path: " << next_param << std::endl;
+ std::cout << "Report will be generated on current directory," << std::endl;
+ i++;
+ }
+ } else if (!strcmp(param, PARAM_HELP)) {
+ emocheck::PrintBanner();
+ emocheck::PrintHelp();
+ return 0;
+ } else {
+ std::cout << "Invalid parameter: " << param << std::endl;
+ return 0;
+ }
+ } else {
+ const char *param = &argv[i][0];
+ std::cout << "Invalid parameter: " << param << std::endl;
+ return 0;
+ }
+ }
+ if (!is_quiet) {
+ emocheck::PrintBanner();
+ }
+ scan_result = emocheck::ScanEmotet(is_debug);
+ if (!is_quiet) {
+ emocheck::PrintReport(scan_result);
+ }
+ if (!is_debug) {
+ if (is_json) {
+ emocheck::JsonReport(scan_result, is_quiet, output_path);
+ } else {
+ emocheck::WriteReport(scan_result, is_quiet, output_path);
+ }
+ }
+ if (!is_quiet) {
+ system("pause");
+ }
return 0;
}