From 547e363b68f5147c459f534f98a9546a97a82d17 Mon Sep 17 00:00:00 2001
From: NYAN CAT <32758426+NYAN-x-CAT@users.noreply.github.com>
Date: Mon, 16 Oct 2023 17:20:42 +0300
Subject: [PATCH] Create UnsafeStreamCodec.cs
---
.../Server/StreamLibrary/UnsafeStreamCodec.cs | 378 ++++++++++++++++++
1 file changed, 378 insertions(+)
create mode 100644 AsyncRAT-C#/Server/StreamLibrary/UnsafeStreamCodec.cs
diff --git a/AsyncRAT-C#/Server/StreamLibrary/UnsafeStreamCodec.cs b/AsyncRAT-C#/Server/StreamLibrary/UnsafeStreamCodec.cs
new file mode 100644
index 00000000..cd488238
--- /dev/null
+++ b/AsyncRAT-C#/Server/StreamLibrary/UnsafeStreamCodec.cs
@@ -0,0 +1,378 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+namespace Server.StreamLibrary
+{
+ public class UnsafeStreamCodec : IDisposable
+ {
+ internal Size Resolution { get; private set; }
+ internal Size CheckBlock { get; private set; }
+ internal int ImageQuality
+ {
+ get { return _imageQuality; }
+ private set
+ {
+ lock (_imageProcessLock)
+ {
+ _imageQuality = value;
+
+ if (_jpgCompression != null)
+ {
+ _jpgCompression.Dispose();
+ }
+
+ _jpgCompression = new JpgCompression(_imageQuality);
+ }
+ }
+ }
+
+ private int _imageQuality;
+ private byte[] _encodeBuffer;
+ private Bitmap _decodedBitmap;
+ private PixelFormat _encodedFormat;
+ private int _encodedWidth;
+ private int _encodedHeight;
+ private readonly object _imageProcessLock = new object();
+ private JpgCompression _jpgCompression;
+
+ ///
+ /// Initialize a new instance of UnsafeStreamCodec class.
+ ///
+ /// The quality to use between 0-100.
+ /// The monitor used for the images.
+ /// The resolution of the monitor.
+ internal UnsafeStreamCodec(int imageQuality)
+ {
+ this.ImageQuality = imageQuality;
+ this.CheckBlock = new Size(50, 1);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+
+ // Tell the Garbage Collector to not waste time finalizing this object
+ // since we took care of it.
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (_decodedBitmap != null)
+ {
+ _decodedBitmap.Dispose();
+ }
+
+ if (_jpgCompression != null)
+ {
+ _jpgCompression.Dispose();
+ }
+ }
+ }
+
+ internal unsafe void CodeImage(IntPtr scan0, Rectangle scanArea, Size imageSize, PixelFormat format,
+ Stream outStream)
+ {
+ lock (_imageProcessLock)
+ {
+ byte* pScan0;
+ if (IntPtr.Size == 8)
+ {
+ // 64 bit process
+ pScan0 = (byte*)scan0.ToInt64();
+ }
+ else
+ {
+ // 32 bit process
+ pScan0 = (byte*)scan0.ToInt32();
+ }
+
+ if (!outStream.CanWrite)
+ {
+ throw new Exception("Must have access to Write in the Stream");
+ }
+
+ int stride = 0;
+ int rawLength = 0;
+ int pixelSize = 0;
+
+ switch (format)
+ {
+ case PixelFormat.Format24bppRgb:
+ case PixelFormat.Format32bppRgb:
+ pixelSize = 3;
+ break;
+ case PixelFormat.Format32bppArgb:
+ case PixelFormat.Format32bppPArgb:
+ pixelSize = 4;
+ break;
+ default:
+ throw new NotSupportedException(format.ToString());
+ }
+
+ stride = imageSize.Width * pixelSize;
+ rawLength = stride * imageSize.Height;
+
+ if (_encodeBuffer == null)
+ {
+ this._encodedFormat = format;
+ this._encodedWidth = imageSize.Width;
+ this._encodedHeight = imageSize.Height;
+ this._encodeBuffer = new byte[rawLength];
+
+ fixed (byte* ptr = _encodeBuffer)
+ {
+ byte[] temp = null;
+ using (Bitmap tmpBmp = new Bitmap(imageSize.Width, imageSize.Height, stride, format, scan0))
+ {
+ temp = _jpgCompression.Compress(tmpBmp);
+ }
+
+ outStream.Write(BitConverter.GetBytes(temp.Length), 0, 4);
+ outStream.Write(temp, 0, temp.Length);
+ NativeMethods.memcpy(new IntPtr(ptr), scan0, (uint)rawLength);
+ }
+ return;
+ }
+
+ if (this._encodedFormat != format)
+ {
+ throw new Exception("PixelFormat is not equal to previous Bitmap");
+ }
+ else if (this._encodedWidth != imageSize.Width || this._encodedHeight != imageSize.Height)
+ {
+ throw new Exception("Bitmap width/height are not equal to previous bitmap");
+ }
+
+ long oldPos = outStream.Position;
+ outStream.Write(new byte[4], 0, 4);
+ long totalDataLength = 0;
+
+ List blocks = new List();
+
+ Size s = new Size(scanArea.Width, CheckBlock.Height);
+ Size lastSize = new Size(scanArea.Width % CheckBlock.Width, scanArea.Height % CheckBlock.Height);
+
+ int lasty = scanArea.Height - lastSize.Height;
+ int lastx = scanArea.Width - lastSize.Width;
+
+ Rectangle cBlock = new Rectangle();
+ List finalUpdates = new List();
+
+ s = new Size(scanArea.Width, s.Height);
+
+ fixed (byte* encBuffer = _encodeBuffer)
+ {
+ var index = 0;
+
+ for (int y = scanArea.Y; y != scanArea.Height; y += s.Height)
+ {
+ if (y == lasty)
+ {
+ s = new Size(scanArea.Width, lastSize.Height);
+ }
+
+ cBlock = new Rectangle(scanArea.X, y, scanArea.Width, s.Height);
+
+ int offset = (y * stride) + (scanArea.X * pixelSize);
+
+ if (NativeMethods.memcmp(encBuffer + offset, pScan0 + offset, (uint)stride) != 0)
+ {
+ index = blocks.Count - 1;
+
+ if (blocks.Count != 0 && (blocks[index].Y + blocks[index].Height) == cBlock.Y)
+ {
+ cBlock = new Rectangle(blocks[index].X, blocks[index].Y, blocks[index].Width,
+ blocks[index].Height + cBlock.Height);
+ blocks[index] = cBlock;
+ }
+ else
+ {
+ blocks.Add(cBlock);
+ }
+ }
+ }
+
+ for (int i = 0; i < blocks.Count; i++)
+ {
+ s = new Size(CheckBlock.Width, blocks[i].Height);
+
+ for (int x = scanArea.X; x != scanArea.Width; x += s.Width)
+ {
+ if (x == lastx)
+ {
+ s = new Size(lastSize.Width, blocks[i].Height);
+ }
+
+ cBlock = new Rectangle(x, blocks[i].Y, s.Width, blocks[i].Height);
+ bool foundChanges = false;
+ uint blockStride = (uint)(pixelSize * cBlock.Width);
+
+ for (int j = 0; j < cBlock.Height; j++)
+ {
+ int blockOffset = (stride * (cBlock.Y + j)) + (pixelSize * cBlock.X);
+
+ if (NativeMethods.memcmp(encBuffer + blockOffset, pScan0 + blockOffset, blockStride) != 0)
+ {
+ foundChanges = true;
+ }
+
+ NativeMethods.memcpy(encBuffer + blockOffset, pScan0 + blockOffset, blockStride);
+ //copy-changes
+ }
+
+ if (foundChanges)
+ {
+ index = finalUpdates.Count - 1;
+
+ if (finalUpdates.Count > 0 &&
+ (finalUpdates[index].X + finalUpdates[index].Width) == cBlock.X)
+ {
+ Rectangle rect = finalUpdates[index];
+ int newWidth = cBlock.Width + rect.Width;
+ cBlock = new Rectangle(rect.X, rect.Y, newWidth, rect.Height);
+ finalUpdates[index] = cBlock;
+ }
+ else
+ {
+ finalUpdates.Add(cBlock);
+ }
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < finalUpdates.Count; i++)
+ {
+ Rectangle rect = finalUpdates[i];
+ int blockStride = pixelSize * rect.Width;
+
+ Bitmap tmpBmp = null;
+ BitmapData tmpData = null;
+ long length;
+
+ try
+ {
+ tmpBmp = new Bitmap(rect.Width, rect.Height, format);
+ tmpData = tmpBmp.LockBits(new Rectangle(0, 0, tmpBmp.Width, tmpBmp.Height),
+ ImageLockMode.ReadWrite, tmpBmp.PixelFormat);
+
+ for (int j = 0, offset = 0; j < rect.Height; j++)
+ {
+ int blockOffset = (stride * (rect.Y + j)) + (pixelSize * rect.X);
+ NativeMethods.memcpy((byte*)tmpData.Scan0.ToPointer() + offset, pScan0 + blockOffset, (uint)blockStride);
+ //copy-changes
+ offset += blockStride;
+ }
+
+ outStream.Write(BitConverter.GetBytes(rect.X), 0, 4);
+ outStream.Write(BitConverter.GetBytes(rect.Y), 0, 4);
+ outStream.Write(BitConverter.GetBytes(rect.Width), 0, 4);
+ outStream.Write(BitConverter.GetBytes(rect.Height), 0, 4);
+ outStream.Write(new byte[4], 0, 4);
+
+ length = outStream.Length;
+ long old = outStream.Position;
+
+ _jpgCompression.Compress(tmpBmp, ref outStream);
+
+ length = outStream.Position - length;
+
+ outStream.Position = old - 4;
+ outStream.Write(BitConverter.GetBytes(length), 0, 4);
+ outStream.Position += length;
+ }
+ finally
+ {
+ tmpBmp.UnlockBits(tmpData);
+ tmpBmp.Dispose();
+ }
+
+ totalDataLength += length + (4 * 5);
+ }
+
+ outStream.Position = oldPos;
+ outStream.Write(BitConverter.GetBytes(totalDataLength), 0, 4);
+ }
+ }
+
+ internal unsafe Bitmap DecodeData(IntPtr codecBuffer, uint length)
+ {
+ if (length < 4)
+ {
+ return _decodedBitmap;
+ }
+
+ int dataSize = *(int*)(codecBuffer);
+
+ if (_decodedBitmap == null)
+ {
+ byte[] temp = new byte[dataSize];
+
+ fixed (byte* tempPtr = temp)
+ {
+ NativeMethods.memcpy(new IntPtr(tempPtr), new IntPtr(codecBuffer.ToInt32() + 4), (uint)dataSize);
+ }
+
+ this._decodedBitmap = (Bitmap)Bitmap.FromStream(new MemoryStream(temp));
+
+ return _decodedBitmap;
+ }
+ else
+ {
+ return _decodedBitmap;
+ }
+ }
+
+ internal Bitmap DecodeData(Stream inStream)
+ {
+ byte[] temp = new byte[4];
+ inStream.Read(temp, 0, 4);
+ int dataSize = BitConverter.ToInt32(temp, 0);
+
+ if (_decodedBitmap == null)
+ {
+ temp = new byte[dataSize];
+ inStream.Read(temp, 0, temp.Length);
+ this._decodedBitmap = (Bitmap)Bitmap.FromStream(new MemoryStream(temp));
+
+ return _decodedBitmap;
+ }
+
+ using (Graphics g = Graphics.FromImage(_decodedBitmap))
+ {
+ while (dataSize > 0)
+ {
+ byte[] tempData = new byte[4 * 5];
+ inStream.Read(tempData, 0, tempData.Length);
+
+ Rectangle rect = new Rectangle(BitConverter.ToInt32(tempData, 0), BitConverter.ToInt32(tempData, 4),
+ BitConverter.ToInt32(tempData, 8), BitConverter.ToInt32(tempData, 12));
+ int updateLen = BitConverter.ToInt32(tempData, 16);
+
+ byte[] buffer = new byte[updateLen];
+ inStream.Read(buffer, 0, buffer.Length);
+
+ using (MemoryStream m = new MemoryStream(buffer))
+ {
+ using (Bitmap tmp = (Bitmap)Image.FromStream(m))
+ {
+ g.DrawImage(tmp, rect.Location);
+ }
+ }
+
+ dataSize -= updateLen + (4 * 5);
+ }
+ }
+ Resolution = _decodedBitmap.Size;
+ return _decodedBitmap;
+ }
+ }
+}