6-转换

一、前言

关于shellcode如何转UUID、MAC、IPV4、IPV6,我就不多说了,避免重复造轮子。shellcode形式的转换已经被杀的不要不要的,需要配合 混淆加密分离 这两小节才能达到一个不错的静态免杀的效果。但是对于Defender而言,这类shellcode转换和加载被严格地限制,只要你敢用它就敢杀,即使你过了Defender的静态查杀,还是会被它的动态查杀给杀掉,所以这一节只是给读者提供另类的shellcode的转换和加载器的实现思路而已。

shellcode的执行分为三个步骤

  1. 为shellcode分配内存

  2. 将shellcode移动到已分配的内存区域

  3. 执行shellcode

就这三个步骤就延伸出各种各样的玩法,我将在下文介绍区别于之前提到shellcode加载器。

参考文章

1、 CS shellcode内存加载器免杀及实现-安全KER - 安全资讯平台arrow-up-right

2、免杀对抗—内存加载&UUID标识&IPV4地址&MAC地址_uuid mac ipv4-CSDN博客arrow-up-right

3、【免杀2】过静态shellcode编码(降熵) | CN-SEC 中文网arrow-up-right

4、奇安信攻防社区-CS免杀-MAC加载器arrow-up-right

转换工具GitHub - Haunted-Banshee/Shellcode-Hastur: Shellcode Reductio Entropy Toolsarrow-up-right

将shellcode输出*.bin文件(如果有必要的话)

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

// shellcode
unsigned char shellcode[] = {
    0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // NOP 指令
    // 其他 shellcode 字节...
};

int main() {
    // 指定输出文件名
    const char* filename = "output.bin";

    // 打开文件以进行写入(二进制模式)
    FILE *file = fopen(filename, "wb");
    if (file == NULL) {
        perror("Failed to open file");
        return EXIT_FAILURE;
    }

    // 获取 shellcode 的大小
    size_t shellcode_size = sizeof(shellcode);

    // 将 shellcode 写入文件
    size_t written = fwrite(shellcode, sizeof(unsigned char), shellcode_size, file);
    if (written != shellcode_size) {
        perror("Failed to write to file");
        fclose(file);
        return EXIT_FAILURE;
    }

    // 关闭文件
    fclose(file);
    printf("Shellcode written to %s successfully.\n", filename);

    return EXIT_SUCCESS;
}

Shellcode-Hastur 工具使用说明,就以生成UUID形式的shellcode为例子:

生成的是字符串格式的uuid的形式。

二、UUID

UUID(Universally Unique Identifier,通用唯一识别码)是一种标准的标识符格式,用于在计算机系统中唯一标识信息。UUID 的长度通常为 128 位(16 字节),通常以 32 个十六进制数字表示

一说到UUID就会让人联想到 GUIDGUID(全局唯一标识符,Globally Unique Identifier)是一种用于在计算机系统中唯一标识对象的标识符。GUID 通常用于数据库、COM(组件对象模型)、文件系统等场合,确保每个对象都有一个唯一的标识符。GUID常用于系统提权,废话少说回归正题。

有几个重要的WindowsAPI需要关注

  1. HeapCreate:创建可由调用进程使用的专用堆对象。 函数在进程的虚拟地址空间中保留空间,并为此块的指定初始部分分配物理存储。官方文档:HeapCreate 函数 (heapapi.h) - Win32 apps | Microsoft Learnarrow-up-right

  2. HeapAlloc:从堆中分配内存块。官方文档:heapAlloc 函数 (heapapi.h) - Win32 apps | Microsoft Learnarrow-up-right

  3. UuidFromStringA:它能够将字符串形式的uuid转换成二进制。详细语法和功能,参考官方文档:UuidFromStringA 函数 (rpcdce.h) - Win32 apps | Microsoft Learnarrow-up-right

  4. EnumSystemLocalesA,可以回调一个指定的函数指针。关于回调函数执行shellcode,我将在 基于回调函数执行shellcode 详细讲解。语法和功能,参考官方文档:EnumSystemLocalesA 函数 (winnls.h) - Win32 apps | Microsoft Learnarrow-up-right

2.1 C++

sizeof(UUidCode) 用于获取数组 UUidCode 的总字节大小。由于 UUidCode 是一个指向字符串的指针数组,每个元素都是一个 const char* 类型的指针,因此 sizeof(UUidCode) 返回的是指针数组的大小,而不是所有字符串的总大小。每一个字符串是16个字节的大小,所以所有字符串的总大小为sizeof(UUidCode)*16

2.2 CSharp

2.3 Go

  1. RPC_CSTR的定义 typedef unsigned char* RPC_CSTR,所以在Go中我们要将 uuidStrstring 类型转换成 []byte 类型。UuidFromStringA.Call的第一个参数是 uintptr(unsafe.Pointer(&uuidbytes[0])), 用 & 来取字节数组的首地址,再将首地址转换成uintptr类型。它背后的数据转换逻辑我在 2-WindowsAPI 已经讲过。

三、MAC

MAC 地址(媒体访问控制地址,Media Access Control Address),也称物理地址,是一种用于在网络中唯一标识设备的地址。MAC 地址通常以 48 位(6 字节)表示。

一个重要的WidnowsAPI

  1. RtlEthernetStringToAddressA:将以太网 MAC 地址的字符串表示形式转换为以太网地址的二进制格式。官方文档:RtlEthernetStringToAddressA 函数 (ip2string.h) - Win32 apps | Microsoft Learnarrow-up-right

3.1 C++

PS:

  1. 不知道为什么一定要加 typedef LONG NTSTATUS 而且一定要在 #include <ip2string.h> 的前面。我看参考文章没有加,我就调了一下午代码还是没成功,最后加了才成功,给我搞🤮了。

  2. 要编译成64位,32位与库冲突,会报大量错误。

3.2 CSharp

3.3 Go

四、IPV4

IPv4是一种无连接的协议,IPv4使用32位(4字节)地址

一个重要WindowsAPI:

  1. RtlIpv4StringToAddressA:将 IPv4 地址的字符串表示形式转换为二进制 IPv4 地址。官方文档:RtlIpv4StringToAddressA 函数 (ip2string.h) - Win32 apps | Microsoft Learnarrow-up-right

4.1 C++

4.2 CSharp

4.3 Go

五、 IPV6

IPv6(Internet Protocol version 6)是互联网协议(IP)的第6版,是IPv4的后继者。IPv6使用128位地址表示

一个重要WindowsAPI

  1. RtlIpv6StringToAddressA:将 IPv6 地址的字符串表示形式转换为二进制 IPv6 地址

5.1 C++

5.2 CSharp

5.3 Go

Last updated