+- Hack Forums (https://hackforums.net)
+-- Forum: Hacks, Exploits, and Various Discussions (https://hackforums.net/forumdisplay.php?fid=45)
+--- Forum: Blackhat • Red Team • Offensive Security (https://hackforums.net/forumdisplay.php?fid=447)
+---- Forum: Blackhat Training (https://hackforums.net/forumdisplay.php?fid=405)
+----- Forum: Reverse Engineering (https://hackforums.net/forumdisplay.php?fid=229)
+----- Thread: [The Brotherhood] Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC (/showthread.php?tid=6267630)
Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC - Hackers - Apr 4, 2024
"Reverse Engineering" Virus Total
& Hybrid Analysis
(C++ POC)
Today we will be seeing if we can identify any indicators on Virus Total or Hybrid Analysis that could help us identify when our software is being analyzed on their platform.
I started this POC without knowing if I would be successful, but I quickly figured out how to get this POC started.
The goal? To see what modules are being loaded into a program on a runtime scanning service to identify how they analyze software. Let me show you what I mean...
Note: I used .bat to increase detects and make sure each file shows up in the written/dropped files list - I did use .txt in some tests, but this may not always show up in scan details, so stick to .bat or even .exe if you want. To make sure each file is detected better, I threw in the EICAR test string to hopefully trigger more AV actions. The EICAR string didn't trigger all scans like I hoped, but it did increase detects *slightly*.
When a program runs on your computer, it may have its dependencies already included (static build), or it may dynamically link/include them when it starts up in runtime. Either way, C/C++ programs (or C# and others) will load in "modules" so that it has all of the functionality it needs. When you run your software on your own computer, you can easily see what modules are loaded by using the enumerated module list from the program. If you setup a loop to check the loaded module list, you can easily print out the names to understand what is running.
For example, if I run my software on my protected Desktop folder, I can see that AVG (an AV software I have) automatically adds 'aswhook.dll' in runtime.
However, if I run the same exact program in my whitelisted folder (a personal C++ folder that is not scanned by WD or AVG), I do not have this module loaded.
This is because AVG doesn't scan the program when it's in my whitelisted directories, so they do not load their hook into my program. This is an example of what we will be trying to find out: What modules load on Virus Total or Hybrid Analysis that don't load locally? I set out to answer the question, Can we identify any unique modules that only load when running in a VT or HA virtual machine / sandbox?
To detect loaded modules on any computer, VM, etc, we must set up a loop that can check the loaded modules:
Code
while (true) {
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
char szModName[MAX_PATH];
// Get the full path to the module's file
if (GetModuleFileNameExA(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(char))) {
std::string moduleName(szModName);
// Check if the module is already tracked
if (!isModuleTracked(trackedModules, moduleName)) {
// Not tracked, so add it to the list and create a log event for it
trackedModules.push_back(moduleName);
logEvent("Module loaded: " + moduleName, getBasename(moduleName));
createModuleFile(moduleName, hMods[i]);
}
}
}
}
else {
std::cerr << "Failed to enumerate process modules." << std::endl;
break; // Exit the loop if module enumeration fails
}
Sleep(1000); // Sleep for 1 second to reduce CPU load
}
This is just some basic code using default Windows functionality. It calls the createModuleFile function which is important for tracking on all scanners. Many scanning tools will not report what modules were loaded by their programs, so to track all I came up with an idea: save the module names & info to txt files so they show up in scans as 'written files' or 'dropped files' or similar. This will let us use the file list as our visual list of loaded modules, so we can see what was used on scans!
I then modified this to include more info, such as the address calling it, and the filepath. The idea is to add more info so that we can start to identify as much as possible about each module.
We can get something like this - the .txt and info in the [ ] brackets are from me, trying to get more info about each. This can definitely be improved further.
What we are looking for is modules that only load on 1 platform, to see if we can identify any software that is analyzing our program in runtime. Boom, found one!
frida-agent is suspicious because it's associated with Frida, a free software for runtime analysis. We can basically assume that if frida is loaded into our program, its being debugged/analyzed/etc.
Another indicator for your program being debugged in runtime is anything related to Flt - the filter manager for Windows that lets AV use minifilters to detect system changes & actions.
All in all, this POC is only intended to give us the data - it's up to you how you want to use this information. I think that there could easily be some work done to detect certain settings that are used, so your program knows 100% if it's in VT, HA, or another platform's sandbox modes. This may be useful to you. Keep in mind also that it might not always be a case of "this module was loaded" and may actually be related to not just which module is loaded, but when it is loaded, how it's loaded (order, from where, etc), and other context should be taken into account besides just looking for module names. Best of luck! :)
Here is an example of what it may look like if you have systeminfo and also the timestamp in the filename. We can see that it used Windows 7 (weird), x64, with 1GB of RAM. Keep in mind that this may be 'fake' to trick programs into triggering "all features" if they think they will work on an older Win7 machine? Not sure, but it was a surprise to see such an old version in use for this.
There are many directions to go from here, but I hope this POC helps you understand how the filelist on Virus Total, Hybrid Analysis, and similar scanners can be used to track information. Because they print all written files, you can simply use the filenames as a personal log for any runtime data you want :)
Here is the full code to the POC, which has everything needed for the module detection, system info, RAM amount, architecture, etc saved to the filename.
std::string generateFileName(const std::string& moduleName, HMODULE moduleHandle) {
std::wstring processName = getProcessName(); // Get the name of the current process in wide string
std::wstring wideModuleName(moduleName.begin(), moduleName.end()); // Convert moduleName to wide string
std::wstring fileName = processName + L"_" + wideModuleName + L".txt"; // Combine into wide string filename
return wideStringToNarrowString(fileName); // Convert the wide string filename back to a narrow string
}
void createModuleFile(const std::string& moduleName, HMODULE moduleHandle) {
std::string fileName = generateFileName(moduleName, moduleHandle);
std::wstring wideFileName = stringToWideString(fileName);
std::wofstream file(wideFileName);
if (file.is_open()) {
wchar_t exePath[MAX_PATH];
if (GetModuleFileNameW(NULL, exePath, MAX_PATH)) {
std::wstring executableName = exePath; // This will contain the full path of the executable in wide characters
// Convert the executable name to a narrow string for output (optional, based on your needs)
std::vector<char> narrowPath(MAX_PATH);
WideCharToMultiByte(CP_UTF8, 0, exePath, -1, &narrowPath[0], MAX_PATH, NULL, NULL);
std::string narrowExecutableName(narrowPath.begin(), narrowPath.end());
// Write the module name and the name/path of the executable to the file
file << stringToWideString(moduleName + " is loaded by " + narrowExecutableName + ".\n");
}
else {
file << stringToWideString(moduleName + " is loaded.\n"); // Fallback in case GetModuleFileNameW fails
}
std::string eicarTestString = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";
file << stringToWideString(eicarTestString); // Add the EICAR string to the file contents
file.close();
std::wcout << L"File created! : " << wideFileName << std::endl;
}
else {
std::wcerr << L"Unable to create file for module! : " << wideFileName << std::endl;
}
}
int main() {
HANDLE hProcess = GetCurrentProcess(); // Get a handle to the current process
if (hProcess == NULL) {
std::cerr << "Failed to get current process handle." << std::endl;
return 1; // Early exit if unable to get process handle
}
while (true) { // Continuous loop, consider adding a condition for termination
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
char szModName[MAX_PATH];
// Get the full path to the module's file
if (GetModuleFileNameExA(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(char))) {
std::string moduleName(szModName);
// Check if the module is already tracked
if (!isModuleTracked(trackedModules, moduleName)) {
// Not tracked, so add it to the list and create a log event for it
trackedModules.push_back(moduleName);
logEvent("Module loaded: " + moduleName, getBasename(moduleName));
createModuleFile(moduleName, hMods[i]);
}
}
}
}
else {
std::cerr << "Failed to enumerate process modules." << std::endl;
break; // Exit the loop if module enumeration fails
}
// Consider adding a sleep delay here to reduce CPU usage
Sleep(1000); // Sleep for 1 second to reduce CPU load
}
CloseHandle(hProcess); // Clean up the process handle
return 0;
}
I hope you found this useful - please feel free to ask any questions you may have in this thread here :)
If you want to PM or convo then please start the PM or convo with me once you are upgraded & able to. Thanks!
RE: Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC - ttbr0 - Apr 4, 2024
amazing work as always. keen to see your next research
RE: Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC - InspectorGadget - Apr 4, 2024
Nice work mate - keep it up!
RE: Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC - Hackers - Apr 4, 2024
(Apr 4, 2024 04:58 AM)ttbr0 Wrote: amazing work as always. keen to see your next research
Thanks man, appreciate it!
Let me know if you have questions
(Apr 4, 2024 05:28 AM)InspectorGadget Wrote: Nice work mate - keep it up!
Thanks, got my brain juices flowing this morning with this - had started last night but got too tired to reasonably keep coding.
RE: Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC - ttbr0 - Apr 4, 2024
(Apr 4, 2024 06:13 AM)nodes Wrote: Thanks man, appreciate it!
Let me know if you have questions
no questions but it did remind me of this talk i watched a long while ago
RE: Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC - Hackers - Apr 10, 2024
(Apr 4, 2024 06:51 AM)ttbr0 Wrote: no questions but it did remind me of this talk i watched a long while ago
Yep, this sounds similar!
I didn't watch the whole thing, but yeah the concept is basically the same.... scanners want to detect network, file, etc activity and report it. So it's easy to manipulate that into revealing information. If they wanted to prevent this they'd have to hide certain activity from their reports, which could then in turn be manipulated by hackers to use those "hidden" pieces to hide their exploits from reports. Scanners are in a lose-lose. If they hide stuff, reports are lower quality - if they keep as-is, people can use it to RE their environments.
RE: Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC - RelaxMax - Apr 11, 2024
Nice work Sir, i found this useful for scan module
RE: Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC - Hackers - Apr 11, 2024
(Apr 11, 2024 10:18 AM)RelaxMax Wrote: Nice work Sir, i found this useful for scan module
This next piece is still a work in progress, but maybe worth exploring too:
// Query Group Value
bufferSize = sizeof(groupValue);
if (RegQueryValueExA(hSubKey, "Group", NULL, NULL, (LPBYTE)&groupValue, &bufferSize) != ERROR_SUCCESS || strstr(groupValue, "FSFilter") == NULL) {
RegCloseKey(hSubKey);
return;
}
// Check for Parameters subkey existence
HKEY hParametersKey;
if (RegOpenKeyExA(hSubKey, "Parameters", 0, KEY_READ, &hParametersKey) == ERROR_SUCCESS) {
hasParametersSubkey = true;
RegCloseKey(hParametersKey);
}
else {
RegCloseKey(hSubKey);
return; // Skip if Parameters subkey does not exist
}
// Query ImagePath
bufferSize = sizeof(buffer);
if (RegQueryValueExA(hSubKey, "ImagePath", NULL, NULL, (LPBYTE)&buffer, &bufferSize) == ERROR_SUCCESS) {
imagePath.assign(buffer, bufferSize - 1); // Exclude the null terminator
if (!SimplePathMatch(imagePath)) {
RegCloseKey(hSubKey);
return; // Skip if ImagePath does not match the convention
}
}
// Query DependOnService
bufferSize = sizeof(buffer);
if (RegQueryValueExA(hSubKey, "DependOnService", NULL, NULL, (LPBYTE)&buffer, &bufferSize) == ERROR_SUCCESS) {
dependOnService.assign(buffer, bufferSize - 1); // Exclude the null terminator
if (dependOnService.find("FltMgr") == std::string::npos) {
RegCloseKey(hSubKey);
return; // Skip if it does not depend on FltMgr
}
}
// Construct content for the file
infoContent = "Subkey: " + std::string(subkeyName) + "\n";
infoContent += "ImagePath: " + imagePath + "\n";
infoContent += "DependOnService: " + dependOnService + "\n";
// Add more details as needed
// Use the subkey name as the filename, ensuring it's URL-encoded to be filename-safe
std::string encodedName = url_encode(subkeyName);
// Write to a file
writeToFile(encodedName, infoContent);
RegCloseKey(hSubKey);
}
}
int main() {
std::vector<std::string> systemInfo = getSystemInfo();
for (const auto& item : systemInfo) {
writeToFile(item, item); // Writes each item of system info to a separate file
}
HKEY hKey;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
CHAR subkeyName[256];
DWORD subkeyNameSize;
DWORD index = 0;
while (RegEnumKeyExA(hKey, index, subkeyName, &subkeyNameSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
subkeyNameSize = sizeof(subkeyName); // Reset size for the next iteration
QueryAndSaveFilteredServiceInfo(hKey, subkeyName);
index++;
}
RegCloseKey(hKey);
}
else {
std::cerr << "Unable to open registry key." << std::endl;
}
return 0;
}
This reveals more information about the system. You can see here that sometimes specs don't match, which suggests two different sandboxes had two different systems:
This isn't the best data because the specs are too similar to others, but the Windows 6 may be of interest along with specific RAM pairings to detect.
I got simliar results from Hybrid Analysis.
More will be needed for this script here to make sure there is enough data to build a profile on a system.
RE: Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC - Azeta - Oct 20, 2024
I admire your dedication for this POC. I find the program and method interesting. Thanks for the share.
RE: Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC - Hackers - Nov 19, 2024
(Oct 20, 2024 03:52 AM)Azeta Wrote: I admire your dedication for this POC. I find the program and method interesting. Thanks for the share.
Thanks Azeta, much appreciated.
It definitely was just kind of an experiment, but it ended up feeling useful enough to share in a thread.
I'm Stanley HF's helper bot. The OP (original poster) of this thread wants everyone to see this page and has elected to bump it to the top of the showthread listing. Since HF doesn't want a ton of LQP (Low Quality Posts) this is the best method of bumping your content. There is a small βyte fee for each bump. Any OP can bump any of their threads on HF from the linked icon on the top of their first post.
If you haven't already read the first post of this thread we encourage you to do so.
Thanks,
Stanley
RE: Unique Method to RE Scanners ~ Virus Total & Hybrid Analysis ~ C++ POC - Hackers - Mar 16, 2026
This is still a valid concept! Let me know if you have any questions :)