免杀Packer开发(1)

1ce%002025-06-09文章来源:SecHub网络安全社区


设计目标

可以在渗透测试或者攻防演练中,快速针对目标杀软和不同环境进行渗透工具的生成和免杀,让人人都可以用上免杀的工具

设计思路

免杀

自实现或者利用开源工具对exe文件进行处理,exetobin 变成一个shellcode文件

然后利用自己的免杀想法生成loader

每个部分都是一个模块 ,可以组合生成

shellcode加解密 (RC4 XOR AES UUID MAC ....
函数的调用 (proxydll 模块基址...
反沙箱反虚拟机(检测鼠标移动、检测进程数、检测CPU数、检测内存大小、检测微信进程、质数运算真睡眠 ...
内存查杀 (veh 堆加密 堆栈欺骗
QVM(资源 图标 信息 ...
shellcode的执行 (回调 线程 纤程 ...

不同环境

利用 cmake 进行不同环境的exe生成 x86 x64 os …
便于利用后续工具 比如 bof
利用python 来进行生成

后续完善

项目维护 可以增加ui 增加免杀方法…

工具流程

处理 exe -> shellcode
添加模块
python 调用 cmake生成不同平台和环境的

总体框架

每一个模块功能 比如

加解密

proxydll

模块基址

反沙箱反虚拟机

内存查杀

QVM

ASMI ETW

shellcodeLoader

写成一个文件夹 然后里面添加细化的功能代码

lib写处理库和外部调用的库

把每个功能封装成类 用cpp编写 然后在shellcodeLoader中用到这些模块再调用函数

第一阶段

写好加解密库

写好加解密库

格式

base写处理的东西

rc4.hpp 写加密类

demo.cpp 测试使用
eg:

加密用python

RC4

算法介绍:

rc4是一种流加密算法,密钥可变,对称加密

加密过程:

密钥 key,明文 plaintext

  1. KSA 密钥调度算法

    初始化 S = [0, 1, …, 255] 作为状态向量

    获取向量T(如果key输入长度小于256个字节,则进行轮转,直到填满)-> T

    使用 key 对 S 进行置换

     j = 0;   for (i = 0 ; i < 256 ; i++){     j = (j + S[i] + T[i]) mod 256;     swap(S[i] , S[j]);   }
  2. PRGA 伪随机生成算法

    从打乱后的S中生成密钥流K

    生成与 plaintext 等长的密钥流 K

    Ciphertext = plaintext ⊕ K

def rc4_init(key: bytes): key_len = len(key) S = list(range(256)) j = 0 for i in range(256): j = (j+S[i]+key[i%key_len])%256 S[i],S[j] = S[j],S[i] return S def rc4_encrypt(data: bytes,key: bytes): S=rc4_init(key) i=j=0 result = bytearray() for byte in data: i = (i+1) % 256 j = (j+S[i]) % 256 S[i],S[j] = S[j],S[i] K=S[(S[i]+S[j]) % 256] result.append(byte ^ K) return bytes(result) def rc4_test(): key = b'secret_key' data = b'Hello world RC4!' encrypt_data = rc4_encrypt(data,key) print(f'encryptdata : {encrypt_data.hex()}') decrypt_data = rc4_encrypt(encrypt_data,key) print(f'decryptdata : {decrypt_data}') rc4_test()

XOR

算法介绍,XOR动态密钥加密

原始的XOR加密

密文 = 明文 ⊕ 密钥
明文 = 密文 ⊕ 密钥

比较容易破解,所以我们采取动态密钥的方式

引入变量元素:时间戳,随机数,设备id

  1. 固定密钥:secret = “secret”
  2. 动态参数:如当前时间戳 timestamp = “1713341111”
  3. 最终动态密钥 = SHA256(secret + timestamp),或只截取前 N 字节作为最终密钥
  4. 明文与该密钥按字节进行 XOR 操作
import hashlib import time def generateKey(secret :str, timestamp :str, key_len :int) -> bytes: key_src = (secret + timestamp).encode(); hash = hashlib.sha256(key_src).digest(); return hash[:key_len] def xor_encrypt(data :bytes, key :bytes) -> bytes: results = [] for i in range(len(data)): results_byte = data[i] ^ key[i] results.append(results_byte) return bytes(results) secret = "secret_key" plaintext = b"Hello World XOR!" timestamp = str(int(time.time())) key = generateKey(secret,timestamp,len(plaintext)) encrypt = xor_encrypt(plaintext,key) print(key) print("encryptdata: ",encrypt.hex()) key2 = generateKey(secret,timestamp,len(plaintext)) print(key2) decrypt = xor_encrypt(encrypt,key2) print("decryptdata: ",decrypt.decode())

AES

对称加密算法

分组加密 一组128位 16bit

支持三种密钥长度 128 192 256

![image-20250418143235420](/Users/1ce/Library/Application Support/typora-user-images/image-20250418143235420.png)

  • 电码本模式(Electronic Codebook Book (ECB))

  • 密码分组链接模式(Cipher Block Chaining (CBC))

  • 计算器模式(Counter (CTR))

  • 密码反馈模式(Cipher FeedBack (CFB))

  • 输出反馈模式(Output FeedBack (OFB))

分组密码体制:所谓分组密码体制就是指将明文切成一段一段的来加密,然后再把一段一段的密文拼起来形成最终密文的加密方式。AES采用分组密码体制,即AES加密会首先把明文切成一段一段的,而且每段数据的长度要求必须是128位16个字节,如果最后一段不够16个字节了,就需要用Padding来把这段数据填满16个字节,然后分别对每段数据进行加密,最后再把每段加密数据拼起来形成最终的密文。

Padding:Padding就是用来把不满16个字节的分组数据填满16个字节用的,它有三种模式PKCS5、PKCS7和NOPADDING。PKCS5是指分组数据缺少几个字节,就在数据的末尾填充几个字节的几,比如缺少5个字节,就在末尾填充5个字节的5。PKCS7是指分组数据缺少几个字节,就在数据的末尾填充几个字节的0,比如缺少7个字节,就在末尾填充7个字节的0。NoPadding是指不需要填充,也就是说数据的发送方肯定会保证最后一段数据也正好是16个字节。那如果在PKCS5模式下,最后一段数据的内容刚好就是16个16怎么办?那解密端就不知道这一段数据到底是有效数据还是填充数据了,因此对于这种情况,PKCS5模式会自动帮我们在最后一段数据后再添加16个字节的数据,而且填充数据也是16个16,这样解密段就能知道谁是有效数据谁是填充数据了。PKCS7最后一段数据的内容是16个0,也是同样的道理。解密端需要使用和加密端同样的Padding模式,才能准确的识别有效数据和填充数据。我们开发通常采用PKCS7 Padding模式。

初始向量IV:初始向量IV的作用是使加密更加安全可靠,我们使用AES加密时需要主动提供初始向量,而且只需要提供一个初始向量就够了,后面每段数据的加密向量都是前面一段的密文。初始向量IV的长度规定为128位16个字节,初始向量的来源为随机生成。至于为什么初始向量能使加密更安全可靠。

密钥:AES要求密钥的长度可以是128位16个字节、192位或者256位,位数越高,加密强度自然越大,但是加密的效率自然会低一些,因此要做好衡量。我们开发通常采用128位16个字节的密钥,我们使用AES加密时需要主动提供密钥,而且只需要提供一个密钥就够了,每段数据加密使用的都是这一个密钥,密钥来源为随机生成。

四种加密模式:AES一共有四种加密模式,分别是ECB(电子密码本模式)、CBC(密码分组链接模式)、CFB、OFB,我们一般使用的是ECB和CBC模式。四种模式中除了ECB相对不安全之外,其它三种模式的区别并没有那么大

字节替代、行移位、列混淆和轮密钥加

from Crypto.Cipher import AES from Crypto.Util.Padding import pad,unpad from Crypto.Random import get_random_bytes key = b'SecertKeyABCDEFG' iv = get_random_bytes(16) data = b"Hello World AES!" encrypt = AES.new(key,AES.MODE_CBC,iv) encryptHex = encrypt.encrypt(pad(data,AES.block_size)) decrypt = AES.new(key,AES.MODE_CBC,iv) plainText = unpad(decrypt.decrypt(encryptHex),AES.block_size) print("encryptHex: ",encryptHex.hex()) print("decryptStr: ",plainText.decode())

UUID

uuid编码

import uuid def uuid_encode(data: bytes) -> list: count = 16 - len(data) % 16 if len(data) % 16 !=0: data += bytes([count]) * (16 - len(data) % 16) uuids =[] for i in range(0,len(data), 16): block = data[i:i+16] u = uuid.UUID(bytes=block) uuids.append(str(u)) return uuids def uuid_decode(uuids: list) -> bytes: tmp = b''.join([uuid.UUID(u).bytes for u in uuids]) return tmp[:-(int(tmp[-1]))] buf=b"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a" uuids = uuid_encode(buf) for u in uuids: print(str(u)) recover = uuid_decode(uuids) print(recover.hex())

base64

import base64

def Base64_encode(data: bytes) -> bytes:
    return base64.b64encode(data)

def Base64_decode(data: bytes) -> bytes:
    return base64.b64decode(data)

plainText = b"Hello World Base64!"
encoded = Base64_encode(plainText)
decoded = Base64_decode(encoded)

print(encoded)
print(encoded.decode())
print(decoded)
print(decoded.decode())

解密用cpp

RC4

因为是对称加密,再次调用加密函数就行

#include "base.hpp" class rc4 { private: /* data */ int S[256]; int Ken_len; void KSA(const bytes& key) { for (size_t i = 0; i < 256; i++) { /* code */ S[i] = i; } int j = 0; for (size_t i = 0; i < 256; i++) { /* code */ j = (j + S[i] + key[i % key.size()]) % 256; //key[i % key.size()] T() std::swap(S[i],S[j]); } } public: rc4() { } bytes decrypt(const bytes& data,const bytes& key) { Ken_len = key.size(); KSA(key); bytes output; output.reserve(data.size()); int i = 0, j = 0; for (size_t n = 0; n < data.size(); ++n) { i = (i + 1) % 256; j = (j + S[i]) % 256; std::swap(S[i], S[j]); int K = S[(S[i] + S[j]) % 256]; output.push_back(data[n] ^ K); } return output; } };

XOR

#include "base.hpp"
#include  <openssl/sha.h>
class XOR
{
    private:
        size_t key_len;
        bytes sha256(const std::string& input)
        {
            bytes hash(SHA256_DIGEST_LENGTH);
            SHA256((const unsigned char*)input.data(),input.size(),hash.data());
            return hash;
        }
        bytes generate_key(const std::string& secret, const std::string& timestamp, size_t data_len)
        {
            bytes hash = sha256(secret + timestamp);
            bytes key(data_len);
            for (size_t i = 0; i < data_len; i++)
            {
                /* code */
                // 获取与data等长的hash
                key[i] = hash[i % hash.size()];
            }
            return key;
        }
    public:
        XOR()
        {
            
        };
        bytes xor_encrypt(const bytes& data, const std::string& secret)
        {
            bytes key = generate_key(secret,std::to_string(std::time(nullptr)),data.size());
            bytes results(data.size());
            for (size_t i = 0; i < data.size(); i++)
            {
                /* code */
                results[i] = data[i] ^ key[i];
            }
            return results;
        }
};

void xor_demo()
{
    std::string Plaintext = "Hello World XOR!";
    bytes plaintext(Plaintext.begin(),Plaintext.end());
    std::string secret = "secret_key";

    XOR xor1;
    bytes encrypt = xor1.xor_encrypt(plaintext,secret);
    PrintEn(encrypt);
    bytes decrypt = xor1.xor_encrypt(encrypt,secret);
    std::string decryptStr(decrypt.begin(),decrypt.end());
    PrintDe(decryptStr);
}

AES

#include "base.hpp" #include "openssl/aes.h" #include "openssl/rand.h" class AES { public: AES() {} bytes aes_encrypt(const bytes& secret, const bytes& plainText, unsigned char* iv) { AES_KEY en_key; AES_set_encrypt_key(secret.data(), 128, &en_key); int len = plainText.size(); int padding_len = AES_BLOCK_SIZE - (len % AES_BLOCK_SIZE); int total_len = len + padding_len; bytes padded = plainText; padded.insert(padded.end(), padding_len, static_cast<unsigned char>(padding_len)); bytes ciphertext(total_len); AES_cbc_encrypt(padded.data(), ciphertext.data(), total_len, &en_key, iv, AES_ENCRYPT); return ciphertext; } bytes aes_decrypt(const bytes& secret, const bytes& ciphertext, unsigned char* iv) { AES_KEY de_key; AES_set_decrypt_key(secret.data(), 128, &de_key); int len = ciphertext.size(); bytes decrypted(len); AES_cbc_encrypt(ciphertext.data(), decrypted.data(), len, &de_key, iv, AES_DECRYPT); // 去除 PKCS#7 填充 if (!decrypted.empty()) { int pad_len = decrypted.back(); if (pad_len > 0 && pad_len <= AES_BLOCK_SIZE) { decrypted.resize(decrypted.size() - pad_len); } } return decrypted; } };
void aes_demo() { std::string secret = "ThisIsASecretKey"; std::string plainText = "Hello World AES!"; unsigned char iv[AES_BLOCK_SIZE]; RAND_bytes(iv, AES_BLOCK_SIZE); unsigned char iv_copy[AES_BLOCK_SIZE]; memcpy(iv_copy, iv, AES_BLOCK_SIZE); AES aes; bytes encrypt = aes.aes_encrypt(StringtoBytes(secret),StringtoBytes(plainText),iv); PrintEn(encrypt); bytes decrypt = aes.aes_decrypt(StringtoBytes(secret), encrypt,iv_copy); PrintDe(decrypt); }

UUID

#include "base.hpp" #include<sstream> #include<string> #include<iomanip> #include<algorithm> class UUID { private: size_t count; size_t padding_len; std::string generateUUID(bytes& data) { std::ostringstream oss; for (size_t i = 0; i < 16; i++) { if (i == 4 || i == 6 || i == 8 || i == 10) { oss << "-"; } oss << std::hex << std::setw(2) << std::setfill('0') << (int)data[i]; } return oss.str(); } public: stringArray uuid_encrypt(bytes& data) { stringArray array; padding_len = 16 - (data.size() % 16); for (size_t i = 0; i < padding_len; i++) data.push_back(static_cast<unsigned char>(padding_len)); count = data.size() / 16 ; for (size_t i = 0; i < count; ++i) { bytes block(data.begin() + i * 16, data.begin() + (i + 1) * 16); array.push_back(generateUUID(block)); } return array; } bytes uuid_decrypt(stringArray uuids) { bytes results; for (const std::string& uuidStr : uuids) { std::string hexStr = uuidStr; hexStr.erase(std::remove(hexStr.begin(), hexStr.end(), '-'), hexStr.end()); for (size_t i = 0; i < hexStr.size(); i += 2) { std::string byteStr = hexStr.substr(i, 2); results.push_back(static_cast<unsigned char>(std::stoi(byteStr, nullptr, 16))); } } if (results.size() >= padding_len) results.resize(results.size() - padding_len); else results.clear(); return results; } };
void uuid_demo() { bytes shellcode; unsigned char shellcodes[] = { 0x31, 0xc0, // xor %eax, %eax 0x50, // push %eax 0x68, 0x2f, 0x62, 0x69, 0x6e, // push 0x6e69622f 0x68, 0x2f, 0x73, 0x68, 0x00, // push 0x68732f2f 0x89, 0xe3, // mov %esp, %ebx 0x50, // push %eax 0x53, // push %ebx 0x89, 0xe1, // mov %esp, %ecx 0x99, // cltd 0x52, // push %edx 0x53, // push %ebx 0x89, 0xe1, // mov %esp, %ecx 0x52, // push %edx 0x53, // push %ebx 0x89, 0xe1, // mov %esp, %ecx 0x52, // push %edx 0x53, // push %ebx 0x31, 0xc0, // xor %eax, %eax 0x50, // push %eax 0x66, 0x68, 0x63, 0x6d, // push 0x6d63 0x66, 0x89, 0xe1, 0x52, //mov %ecx, %ax }; for (size_t i = 0; i < sizeof(shellcodes); i++) { /* code */ shellcode.push_back(shellcodes[i]); } UUID uuid; stringArray shellcodeArray = uuid.uuid_encrypt(shellcode); PrintStringArray("UUID_encrypt :",shellcodeArray); PrintEn(uuid.uuid_decrypt(shellcodeArray)); }
#include "../lib/encrypt.hpp"
#include <windows.h>
void rc4_demo()
{
    std::string KeyStr = "secret_key";
    std::string Plaintext = "Hello world RC4!";
    bytes key = StringtoBytes(KeyStr);
    bytes plaintext = StringtoBytes(Plaintext);

    RC4 rc4;
    bytes cryptHex = rc4.decrypt(plaintext,key);
    PrintEn(cryptHex);

    bytes decrypt=rc4.decrypt(cryptHex,key);
    PrintDe(decrypt);
}
void xor_demo()
{
    std::string Plaintext = "Hello World XOR!";
    bytes plaintext = StringtoBytes(Plaintext);
    std::string secret = "secret_key";

    XOR xor1;
    bytes encrypt = xor1.xor_encrypt(plaintext,secret);
    PrintEn(encrypt);
    bytes decrypt = xor1.xor_encrypt(encrypt,secret);
    PrintDe(decrypt);
}
void aes_demo()
{
    std::string secret = "ThisIsASecretKey";
    std::string plainText = "Hello World AES!";
    unsigned char iv[AES_BLOCK_SIZE];
    RAND_bytes(iv, AES_BLOCK_SIZE);

    unsigned char iv_copy[AES_BLOCK_SIZE];
    memcpy(iv_copy, iv, AES_BLOCK_SIZE);
    AES aes;
    bytes encrypt = aes.aes_encrypt(StringtoBytes(secret),StringtoBytes(plainText),iv);
    PrintEn(encrypt);
    bytes decrypt = aes.aes_decrypt(StringtoBytes(secret), encrypt,iv_copy);
    PrintDe(decrypt);
}
void uuid_demo()
{
    bytes shellcode;
    unsigned char shellcodes[] = {
    0x31, 0xc0,             // xor    %eax, %eax
    0x50,                   // push   %eax
    0x68, 0x2f, 0x62, 0x69, 0x6e, // push   0x6e69622f
    0x68, 0x2f, 0x73, 0x68, 0x00, // push   0x68732f2f
    0x89, 0xe3,             // mov    %esp, %ebx
    0x50,                   // push   %eax
    0x53,                   // push   %ebx
    0x89, 0xe1,             // mov    %esp, %ecx
    0x99,                   // cltd   
    0x52,                   // push   %edx
    0x53,                   // push   %ebx
    0x89, 0xe1,             // mov    %esp, %ecx
    0x52,                   // push   %edx
    0x53,                   // push   %ebx
    0x89, 0xe1,             // mov    %esp, %ecx
    0x52,                   // push   %edx
    0x53,                   // push   %ebx
    0x31, 0xc0,             // xor    %eax, %eax
    0x50,                   // push   %eax
    0x66, 0x68, 0x63, 0x6d, // push    0x6d63
    0x66, 0x89, 0xe1, 0x52, //mov    %ecx, %ax
};
for (size_t i = 0; i < sizeof(shellcodes); i++)
{
    /* code */
    shellcode.push_back(shellcodes[i]);
}
    UuID uuid;
    stringArray shellcodeArray = uuid.uuid_encrypt(shellcode);
    PrintStringArray("UUID_encrypt :",shellcodeArray);
    PrintEn(uuid.uuid_decrypt(shellcodeArray));
}
void base64_demo()
{
    Base64 base64;
    std::string plainText = "Hello World Base64!";
    bytes encoded =  StringtoBytes(plainText);
    bytes encodedCode = base64.base64_encode(encoded);
    std::cout << "Encoded Base64:" << std::endl;
    PrintDe(encodedCode);
    PrintDe(base64.base64_decode(BytestoString(encodedCode)));
}
void mac_demo()
{
    
    bytes shellcode = {0x31, 0xc0, 0x50, 0x68, 0x2f, 0x2f,
                       0x73, 0x68, 0x89, 0xe3, 0x50, 0x53,
                       0x89, 0xe1, 0x99, 0xb0, 0x0b, 0xcd,
                       0x80};
    MAC mac;
    std::cout << "Encoded MACs:" << std::endl;
    stringArray macs = mac.shellcode_to_mac(shellcode);
    for (const auto& m : macs) {
        std::cout << m << std::endl;
    }
    bytes recovered = mac.mac_to_shellcode(macs);
    PrintEn(recovered);
}
void shellcode_demo(const char* fileName)
{
    HANDLE hFile =  CreateFile(fileName, GENERIC_ALL, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to open file." << std::endl;
        return;
    }
    DWORD fileSize = GetFileSize(hFile, NULL);
    if (fileSize == INVALID_FILE_SIZE) {
        std::cerr << "Failed to get file size." << std::endl;
        CloseHandle(hFile);
        return;
    }
    bytes shellcode(fileSize);
    DWORD bytesRead;
    if (!ReadFile(hFile, shellcode.data(), fileSize, &bytesRead, NULL)) {
        std::cerr << "Failed to read file." << std::endl;
        CloseHandle(hFile);
        return;
    }
    CloseHandle(hFile);
    std::cout << "Shellcode read from file:" << std::endl;
    PrintHex(shellcode);
    Base64 base64;
    auto tmp = base64.base64_encode(shellcode);
    PrintHex(base64.base64_decode(BytestoString(tmp)));
}
int main()
{
    // rc4_demo();
    // xor_demo();
    // aes_demo();
    // uuid_demo();
    // base64_demo();
    // mac_demo();
    shellcode_demo("payload.bin");
    return 0;
}

编译运行