1#include <grpcpp/grpcpp.h>
2#include "panoptes.grpc.pb.h"
21using grpc::ClientContext;
24std::unique_ptr<PanoptesExtensibility::Stub>
stub_;
30namespace fs = std::filesystem;
33 std::string quarantinePath =
"C:\\ProgramData\\Panoptes\\Quarantine";
35 if (!fs::exists(quarantinePath)) {
36 fs::create_directories(quarantinePath);
38 std::filesystem::path sourcePath(filePath);
39 std::filesystem::path destinationPath = fs::path(quarantinePath) / sourcePath.filename();
41 fs::rename(sourcePath, destinationPath);
43 catch (
const std::filesystem::filesystem_error& e) {
44 std::cerr <<
"Error moving file to quarantine: " << e.what() << std::endl;
51 DWORD dwType = REG_DWORD;
55 LONG lResult = RegOpenKeyExA(
63 if (lResult != ERROR_SUCCESS) {
64 std::cerr <<
"Error opening registry key. Error code: " << lResult << std::endl;
69 lResult = RegQueryValueExA(
74 reinterpret_cast<LPBYTE
>(&portValue),
80 if (lResult != ERROR_SUCCESS) {
81 std::cerr <<
"Error reading registry value. Error code: " << lResult << std::endl;
85 if (dwType != REG_DWORD) {
86 std::cerr <<
"Unexpected value type in registry." << std::endl;
95 std::filesystem::path fullPathNormalized = std::filesystem::path(fullPath).lexically_normal();
97 return std::any_of(exclusions.begin(), exclusions.end(),
98 [&fullPathNormalized](
const std::string& path) {
99 std::filesystem::path pathNormalized = std::filesystem::path(path).lexically_normal();
100 return fullPathNormalized.string().find(pathNormalized.string()) == 0;
105 nlohmann::json jsonObject = nlohmann::json::parse(jsonString);
106 if (jsonObject.contains(
"yara_scan") && jsonObject[
"yara_scan"].contains(
"detected_rules"))
108 nlohmann::json yaraObject = jsonObject[
"yara_scan"];
109 int detected_rules = yaraObject[
"detected_rules"].size();
110 if (detected_rules > 0) {
115 if (jsonObject.contains(
"amsi_result"))
132 LONG lResult = RegCreateKeyExA(
134 "SOFTWARE\\Panoptes",
137 REG_OPTION_NON_VOLATILE,
144 if (lResult != ERROR_SUCCESS) {
145 std::cerr <<
"Error creating/opening registry key. Error code: " << lResult << std::endl;
150 lResult = RegSetValueExA(
155 reinterpret_cast<const BYTE*
>(&dwPort),
159 if (lResult != ERROR_SUCCESS) {
160 std::cerr <<
"Error setting registry value. Error code: " << lResult << std::endl;
166 std::cout <<
"Registry entry created successfully." << std::endl;
172 while ((pos = msg.find(
"\\u0000")) != std::string::npos) {
176 nlohmann::json j = nlohmann::json::parse(msg);
177 std::time_t now = std::time(
nullptr);
179 j[
"Time"] = std::string(formattedTime);
180 std::string dumpAgain = j.dump();
186 std::string server_url =
"localhost:" + std::to_string(containerPort);
187 std::shared_ptr<grpc::Channel> channel = grpc::CreateChannel(server_url, grpc::InsecureChannelCredentials());
188 stub_ = PanoptesExtensibility::NewStub(channel);
193 ClientContext g_context;
195 MemoryScanInfo request;
196 request.set_process_id(processId);
198 Status status =
stub_->MemoryScan(&g_context, request, &reply);
201 std::cout << status.error_code() <<
": " << status.error_message()
205 return reply.ack_type();
210 ClientContext context;
213 request.set_file_hash(fileHash);
214 request.set_portable_executable_path(pePath);
216 Status status =
stub_->PEScan(&context, request, &reply);
218 std::cout << status.error_code() <<
": " << status.error_message() << std::endl;
221 return reply.ack_type();
230 DWORD containerPort = 0;
232 std::string server_url =
"localhost:" + std::to_string(containerPort);
233 std::shared_ptr<grpc::Channel> channel = grpc::CreateChannel(server_url, grpc::InsecureChannelCredentials());
234 selfStub_ = PanoptesService::NewStub(channel);
237 ClientContext context;
239 request.set_file_hash(fileHash);
240 request.set_portable_executable_path(pePath);
242 Status status =
selfStub_->QueuePeScan(&context, request, &reply);
244 std::cout << status.error_code() <<
": " << status.error_message() << std::endl;
249 ::grpc::Status ScanResults(::grpc::ServerContext* context, const ::ContainerReply* request, ::AckMessage* response)
override {
250 google::protobuf::util::JsonPrintOptions options;
252 options.add_whitespace =
false;
254 options.preserve_proto_field_names =
true;
255 options.always_print_enums_as_ints =
false;
258 std::string fileHash = request->file_hash();
259 std::string filePath = request->portable_executable_path();
260 std::string json_string;
261 google::protobuf::util::MessageToJsonString(*request, &json_string, options);
267 std::string entry = loadedDB.GetEntry(fileHash);
269 loadedDB.AddEntry(fileHash, cleanMsg);
274 entry = loadedDB.UpdateEntry(fileHash, cleanMsg);
278 if (!request->has_pe_scan()) {
280 std::string displayMessage =
"Malicious File Detected: " +
GetBaseName(filePath);
283 if (configuration->m_quartine) {
289 response->set_ack_type(SUCCESS);
290 return ::grpc::Status::OK;
293 ::grpc::Status QueuePeScan(::grpc::ServerContext* context, const ::PeScanInfo* request, ::AckMessage* response)
override {
294 std::string fileToScan = request->portable_executable_path();
295 if (fileToScan.empty()) {
296 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT,
"PE path not provided");
301 std::string message =
"File is excluded from scan: " + fileToScan;
302 return ::grpc::Status(::grpc::StatusCode::UNKNOWN, message);
305 std::string fileHash = request->file_hash();
306 if (fileHash.empty()) {
308 if (fileHash.empty()) {
309 std::string message =
"Failed to generate hash for " + fileToScan;
310 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, message);
315 std::string entry = loadedDB.GetEntry(fileHash);
319 response->set_ack_type(AckType::SUCCESS);
320 response->set_message(entry);
322 std::string displayMessage =
"Malicious File Detected: " +
GetBaseName(fileToScan.c_str());
325 if (configuration->m_quartine) {
330 if (container.first == ContainerType::CONTAINER_TYPE_PE) {
340 if (container.first == ContainerType::CONTAINER_TYPE_PE) {
348 return ::grpc::Status::OK;
351 ::grpc::Status
HealthCheck(::grpc::ServerContext* context, const ::HealthCheckRequest* request, ::HealthCheckResponse* response)
override {
352 response->set_pong(
"pong");
353 return ::grpc::Status::OK;
356 ::grpc::Status Hello(::grpc::ServerContext* context, const ::ContainerInfo* request, ::AckMessage* response)
override {
357 if (request->container_type() == NULL) {
358 return ::grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT,
"No container type provided");
361 std::cout <<
"Hello: " << request->container_type() << std::endl;
363 std::pair<ContainerType, int> ContainerPortInfo = std::make_pair(request->container_type(), request->grpc_port());
366 response->set_ack_type(AckType::SUCCESS);
367 return ::grpc::Status::OK;
375 grpc::ServerBuilder builder;
376 int selected_port = 0;
377 std::string server_url =
"localhost:0";
380 builder.AddListeningPort(server_url, grpc::InsecureServerCredentials(), &selected_port);
381 builder.RegisterService(&service);
383 std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
384 if (server ==
nullptr) {
386 bool threadState = threadError->load();
387 threadError->store(
true);
393 bool threadState = threadError->load();
394 threadError->store(
true);
AMSI_RESULT_PANO
The result of the AMSI scan.
@ AMSI_RESULT_PANO_DETECTED
PanoptesContainerClient(int ContainerPort)
bool SendMemoryScanRequest(DWORD ProcessId)
bool SendPeScanRequest(std::string PePath, std::string FileHash)
The PanoptesImpl class is a class that implements the PanoptesExtensibility::Service interface from t...
static BOOL ShowTrayIconBalloon(LPCSTR pszTitle, LPCSTR pszText)
Display a tray icon balloon notification.
void HealthCheck()
The HealthCheck function is a thread that checks the health of the Panoptes main service.
PanoptesContext * serviceContext
std::string CleanUpProtobufMessage(std::string msg)
void MoveFileToQuarantine(std::string filePath)
std::unique_ptr< PanoptesExtensibility::Stub > stub_
bool CheckIfMalicious(std::string jsonString)
bool CreateRegistryEntryWithPort(DWORD dwPort)
bool GetRegistryPortValue(DWORD &portValue)
void SelfQueuePeScan(std::string pePath, std::string fileHash)
std::unique_ptr< PanoptesService::Stub > selfStub_
void RunServiceServer(LPVOID lpParam)
std::vector< std::pair< ContainerType, int > > g_containerServerPorts
bool isPathInExclusions(const std::vector< std::string > &exclusions, const std::string &fullPath)
std::string GenerateMD5(std::string filePath)
Generate an MD5 hash of a file using the Windows Crypto API https://learn.microsoft....
void WriteToLogFile(const std::string &message)
std::unique_ptr< PanoptesService::Stub > stub_
std::atomic< bool > threadError
std::atomic< PanoptesDatabase > database
std::string GetBaseName(const std::string &path)
std::string FormatTime(const std::time_t &time)