honey_the_codewitch

C# Serial w/ framing and disconnect detection

Mar 2nd, 2026 (edited)
132
0
Never
7
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 19.06 KB | None | 0 0
  1. using Microsoft.Win32.SafeHandles;
  2.  
  3. using System.Buffers.Binary;
  4. using System.ComponentModel;
  5. using System.Runtime.CompilerServices;
  6. using System.Runtime.InteropServices;
  7.  
  8. namespace Espmon;
  9.  
  10. public sealed class FrameReceivedEventArgs : EventArgs
  11. {
  12.     public byte Command;
  13.     public byte[] Data { get; }
  14.  
  15.     public FrameReceivedEventArgs(byte command, byte[] data)
  16.     {
  17.         ArgumentOutOfRangeException.ThrowIfLessThan(command,128,nameof(command));
  18.         Command = (byte)(command>>1);
  19.         Data = data;
  20.     }
  21. }
  22.  
  23. internal partial class EspSerialSession : IDisposable
  24. {
  25.     struct StateMachine
  26.     {
  27.         int state;
  28.         byte cmd;
  29.         int len;
  30.         uint crc;
  31.         public byte RawCommandByte => cmd;
  32.         public byte Command => (byte)(cmd >> 1);
  33.         public int Length => len;
  34.         public uint Crc => crc;
  35.         public bool IsDone => state == 16;
  36.         public void Reset() => state = 0;
  37.         public bool Step(List<byte>? log, object? logLock, byte data)
  38.         {
  39.             if (state == 0 || state ==16)
  40.             {
  41.                 if (data <128)
  42.                 {
  43.                     if (logLock != null)
  44.                     {
  45.                         lock (logLock)
  46.                         {
  47.                             log?.Add(data);
  48.                         }
  49.                     }
  50.                     return false;
  51.                 }
  52.                 state = 1;
  53.                 cmd = data;
  54.                 len = 0;
  55.             }
  56.             else if (state < 8)
  57.             {
  58.                 if (cmd != data)
  59.                 {
  60.                     if (logLock != null)
  61.                     {
  62.                         lock (logLock)
  63.                         {
  64.                             for (var i = 0; i<state;++i)
  65.                             {
  66.                                 log?.Add(cmd);
  67.                             }
  68.                             log?.Add(data);
  69.                         }
  70.                     }
  71.                     state = 0;
  72.                     return false;
  73.                 }
  74.                 ++state;
  75.             }
  76.             else if (state == 8)
  77.             {
  78.                 len = data;
  79.                 ++state;
  80.             }
  81.             else if (state < 12)
  82.             {
  83.                 len <<= 8;
  84.                 len |= data;
  85.                 ++state;
  86.             }
  87.             else if (state == 12)
  88.             {
  89.                 crc = data;
  90.                 ++state;
  91.             }
  92.             else if (state < 16)
  93.             {
  94.                 crc <<= 8;
  95.                 crc |= data;
  96.                 ++state;
  97.             }
  98.             else if (state == 16)
  99.             {
  100.                 // do nothing "stall"
  101.             }
  102.             else
  103.             {
  104.                 state = 0;
  105.                 return false;
  106.             }
  107.             return true;
  108.         }
  109.     }
  110.  
  111.     /// <summary>
  112.     /// Captures the state for one pending overlapped I/O so the
  113.     /// IOCallback can resolve the right TaskCompletionSource.
  114.     /// </summary>
  115.     private sealed class ReadOp
  116.     {
  117.         public TaskCompletionSource<int> Tcs = null!;
  118.         public unsafe NativeOverlapped* Overlapped;
  119.     }
  120.  
  121.     private sealed class CommEventOp
  122.     {
  123.         public TaskCompletionSource<int> Tcs = null!;
  124.         public unsafe NativeOverlapped* Overlapped;
  125.         public GCHandle MaskHandle;
  126.     }
  127.  
  128.     private volatile bool _closing;
  129.     private bool _disposed;
  130.     SafeFileHandle _handle;
  131.     ThreadPoolBoundHandle _boundHandle;
  132.     readonly List<byte> _log;
  133.     readonly object _lock;
  134.     SynchronizationContext? _sync;
  135.     Task _readTask, _statTask;
  136.     public event EventHandler<EventArgs>? Disconnected;
  137.     public event EventHandler<FrameReceivedEventArgs>? FrameReceived;
  138.     public event EventHandler<FrameReceivedEventArgs>? FrameCrcError;
  139.  
  140.     protected virtual void Dispose(bool disposing)
  141.     {
  142.         if (!_disposed)
  143.         {
  144.             if (disposing)
  145.             {
  146.                 _closing = true;
  147.                 // CancelIoEx unblocks any pending ReadFile / WaitCommEvent.
  148.                 // They will complete with ERROR_OPERATION_ABORTED and the
  149.                 // IOCP callback will fire, resolving the tasks.
  150.                 CancelIoEx(_handle, IntPtr.Zero);
  151.                 Task.WaitAll([_statTask, _readTask]);
  152.                 _boundHandle.Dispose();
  153.                 _handle.Dispose();
  154.             }
  155.             _disposed = true;
  156.         }
  157.     }
  158.  
  159.     ~EspSerialSession()
  160.     {
  161.         Dispose(false);
  162.     }
  163.  
  164.     public void Dispose()
  165.     {
  166.         Dispose(disposing: true);
  167.         GC.SuppressFinalize(this);
  168.     }
  169.  
  170.     public void Send(byte cmd, ReadOnlySpan<byte> data)
  171.     {
  172.         ArgumentOutOfRangeException.ThrowIfGreaterThan(cmd, 127, nameof(cmd));
  173.         cmd <<= 1;
  174.         Span<byte> len = [0, 0, 0, 0];
  175.         BinaryPrimitives.WriteInt32LittleEndian(len, data.Length);
  176.         Span<byte> crc = [0, 0, 0, 0];
  177.         BinaryPrimitives.WriteUInt32LittleEndian(crc, Crc32(data));
  178.         Span<byte> toWrite = [cmd, cmd, cmd, cmd, cmd, cmd, cmd, cmd, .. len, .. crc, .. data];
  179.         WriteAll(toWrite);
  180.     }
  181.  
  182.     private unsafe void WriteAll(ReadOnlySpan<byte> data)
  183.     {
  184.         fixed (byte* ptr = data)
  185.         {
  186.             int offset = 0;
  187.             while (offset < data.Length)
  188.             {
  189.                 var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
  190.                 var ov = _boundHandle.AllocateNativeOverlapped(
  191.                     (errorCode, numBytes, pOv) =>
  192.                     {
  193.                         _boundHandle.FreeNativeOverlapped(pOv);
  194.                         if (errorCode == 0)
  195.                             tcs.TrySetResult((int)numBytes);
  196.                         else
  197.                             tcs.TrySetException(new Win32Exception((int)errorCode));
  198.                     },
  199.                     null, null);
  200.  
  201.                 int written = 0;
  202.                 if (!WriteFile(_handle, ptr + offset, data.Length - offset, ref written, ov))
  203.                 {
  204.                     int err = Marshal.GetLastWin32Error();
  205.                     if (err != ERROR_IO_PENDING)
  206.                     {
  207.                         _boundHandle.FreeNativeOverlapped(ov);
  208.                         throw new Win32Exception(err);
  209.                     }
  210.                     // IO_PENDING — IOCP callback will fire
  211.                 }
  212.                 else
  213.                 {
  214.                     // Completed synchronously, but IOCP callback will still fire
  215.                     // for handles bound to IOCP.  Wait for it.
  216.                 }
  217.  
  218.                 // Block for write completion (Send is synchronous from caller's perspective)
  219.                 written = tcs.Task.GetAwaiter().GetResult();
  220.                 offset += written;
  221.             }
  222.         }
  223.     }
  224.  
  225.     private void OnDisconnected(EventArgs args)
  226.     {
  227.         if (_disposed) return;
  228.         if (Disconnected != null)
  229.         {
  230.             if (_sync == null)
  231.             {
  232.                 Disconnected?.Invoke(this, args);
  233.             }
  234.             else
  235.             {
  236.                 _sync.Post((state) => Disconnected?.Invoke(this, args), null);
  237.             }
  238.         }
  239.     }
  240.  
  241.     public byte[] GetNextLogData()
  242.     {
  243.         lock (_lock)
  244.         {
  245.             var res = _log.ToArray();
  246.             _log.Clear();
  247.             return res;
  248.         }
  249.     }
  250.  
  251.     private void OnFrameReceived(FrameReceivedEventArgs args)
  252.     {
  253.         if (_disposed) return;
  254.         if (FrameReceived != null)
  255.         {
  256.             if (_sync == null)
  257.             {
  258.                 FrameReceived?.Invoke(this, args);
  259.             }
  260.             else
  261.             {
  262.                 _sync.Post((state) => FrameReceived?.Invoke(this, args), null);
  263.             }
  264.         }
  265.     }
  266.  
  267.     private void OnFrameCrcError(FrameReceivedEventArgs args)
  268.     {
  269.         if (_disposed) return;
  270.         if (FrameCrcError != null)
  271.         {
  272.             if (_sync == null)
  273.             {
  274.                 FrameCrcError?.Invoke(this, args);
  275.             }
  276.             else
  277.             {
  278.                 _sync.Post((state) => FrameCrcError?.Invoke(this, args), null);
  279.             }
  280.         }
  281.     }
  282.  
  283.     /// <summary>
  284.     /// Overlapped read via IOCP.  The CLR's thread pool dispatches the
  285.     /// completion callback — no manual event handles or registered waits.
  286.     /// </summary>
  287.     private unsafe Task<int> ReadAsync(byte[] buffer, int offset, int count)
  288.     {
  289.         var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
  290.         var ov = _boundHandle.AllocateNativeOverlapped(
  291.             (errorCode, numBytes, pOv) =>
  292.             {
  293.                 _boundHandle.FreeNativeOverlapped(pOv);
  294.                 if (errorCode == 0)
  295.                     tcs.TrySetResult((int)numBytes);
  296.                 else if (errorCode == ERROR_OPERATION_ABORTED)
  297.                     tcs.TrySetCanceled();
  298.                 else
  299.                     tcs.TrySetException(new Win32Exception((int)errorCode));
  300.             },
  301.             null,
  302.             buffer);  // pins buffer until FreeNativeOverlapped
  303.  
  304.         int read = 0;
  305.         fixed (byte* pBuf = &buffer[offset])
  306.         {
  307.             if (!ReadFile(_handle, pBuf, count, ref read, ov))
  308.             {
  309.                 int err = Marshal.GetLastWin32Error();
  310.                 if (err != ERROR_IO_PENDING)
  311.                 {
  312.                     _boundHandle.FreeNativeOverlapped(ov);
  313.                     if (err == ERROR_OPERATION_ABORTED)
  314.                         tcs.TrySetCanceled();
  315.                     else
  316.                         tcs.TrySetException(new Win32Exception(err));
  317.                 }
  318.                 // else: pending — IOCP callback will fire
  319.             }
  320.             // else: completed synchronously — IOCP callback still fires for bound handles
  321.         }
  322.  
  323.         return tcs.Task;
  324.     }
  325.  
  326.     private async Task ReadExactlyAsync(byte[] buffer, int count)
  327.     {
  328.         int offset = 0;
  329.         while (offset < count)
  330.         {
  331.             int n = await ReadAsync(buffer, offset, count - offset);
  332.             if (n == 0)
  333.                 throw new IOException("Serial port returned 0 bytes (device disconnected).");
  334.             offset += n;
  335.         }
  336.     }
  337.  
  338.     /// <summary>
  339.     /// Overlapped WaitCommEvent via IOCP.
  340.     /// WaitCommEvent writes the event mask to an int — we pin it via GCHandle
  341.     /// and read it in the callback.
  342.     /// </summary>
  343.     private unsafe Task<int> WaitCommEventAsync()
  344.     {
  345.         var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
  346.         var maskArr = new int[1];
  347.         var maskPin = GCHandle.Alloc(maskArr, GCHandleType.Pinned);
  348.  
  349.         var ov = _boundHandle.AllocateNativeOverlapped(
  350.             (errorCode, numBytes, pOv) =>
  351.             {
  352.                 int mask = maskArr[0];
  353.                 maskPin.Free();
  354.                 _boundHandle.FreeNativeOverlapped(pOv);
  355.                 if (errorCode == 0)
  356.                     tcs.TrySetResult(mask);
  357.                 else if (errorCode == ERROR_OPERATION_ABORTED)
  358.                     tcs.TrySetCanceled();
  359.                 else
  360.                     tcs.TrySetException(new Win32Exception((int)errorCode));
  361.             },
  362.             null, null);
  363.  
  364.         if (!WaitCommEvent(_handle, ref maskArr[0], ov))
  365.         {
  366.             int err = Marshal.GetLastWin32Error();
  367.             if (err != ERROR_IO_PENDING)
  368.             {
  369.                 maskPin.Free();
  370.                 _boundHandle.FreeNativeOverlapped(ov);
  371.                 if (err == ERROR_OPERATION_ABORTED)
  372.                     tcs.TrySetCanceled();
  373.                 else
  374.                     tcs.TrySetException(new Win32Exception(err));
  375.             }
  376.         }
  377.  
  378.         return tcs.Task;
  379.     }
  380.  
  381.     public EspSerialSession(string port, bool logging = false, SynchronizationContext? syncContext = null)
  382.     {
  383.         _lock = new object();
  384.         _sync = syncContext;
  385.         _log = new List<byte>();
  386.  
  387.         var rawHandle = CreateFile(
  388.             $@"\\.\{port}",
  389.             GENERIC_READ | GENERIC_WRITE,
  390.             0,
  391.             IntPtr.Zero,
  392.             OPEN_EXISTING,
  393.             FILE_FLAG_OVERLAPPED,
  394.             IntPtr.Zero);
  395.         if (rawHandle.IsInvalid)
  396.         {
  397.             throw new Win32Exception(Marshal.GetLastWin32Error());
  398.         }
  399.         _handle = rawHandle;
  400.  
  401.         // Bind to the CLR's IOCP thread pool.  All overlapped completions
  402.         // on this handle are now dispatched via the thread pool — no need
  403.         // for manual event handles or RegisterWaitForSingleObject.
  404.         _boundHandle = ThreadPoolBoundHandle.BindHandle(_handle);
  405.  
  406.         DCB dcb = default;
  407.         dcb.DCBlength = (uint)Unsafe.SizeOf<DCB>();
  408.         if (!GetCommState(_handle, ref dcb))
  409.         {
  410.             throw new Win32Exception(Marshal.GetLastWin32Error());
  411.         }
  412.  
  413.         dcb.BaudRate = 115200;
  414.         dcb.ByteSize = 8;
  415.         dcb.Parity = 0;
  416.         dcb.StopBits = 0;
  417.  
  418.         if (!SetCommState(_handle, ref dcb))
  419.         {
  420.             throw new Win32Exception(Marshal.GetLastWin32Error());
  421.         }
  422.  
  423.         if (!SetCommMask(_handle, EV_RLSD))
  424.         {
  425.             throw new Win32Exception(Marshal.GetLastWin32Error());
  426.         }
  427.         if(!SetupComm(_handle,8192,0))
  428.         {
  429.             throw new Win32Exception(Marshal.GetLastWin32Error());
  430.         }
  431.         _statTask = Task.Run(async () =>
  432.         {
  433.             try
  434.             {
  435.                 while (!_closing)
  436.                 {
  437.                     int mask = await WaitCommEventAsync();
  438.                     if ((mask & (int)EV_RLSD) != 0 && !_closing)
  439.                     {
  440.                         OnDisconnected(EventArgs.Empty);
  441.                         break;
  442.                     }
  443.                 }
  444.             }
  445.             catch (OperationCanceledException) { }
  446.             catch (Exception)
  447.             {
  448.                 if (!_closing)
  449.                     OnDisconnected(EventArgs.Empty);
  450.             }
  451.         });
  452.  
  453.         _readTask = Task.Run(async () =>
  454.         {
  455.             byte[] tmp = new byte[1];
  456.             StateMachine mach = default;
  457.             try
  458.             {
  459.                 while (!_closing)
  460.                 {
  461.                     await ReadExactlyAsync(tmp, 1);
  462.                     mach.Step(_log, _lock, tmp[0]);
  463.                     if (mach.IsDone)
  464.                     {
  465.                         if (_closing) break;
  466.                         var frame = new byte[mach.Length];
  467.                         await ReadExactlyAsync(frame, mach.Length);
  468.                         if (Crc32(frame) == mach.Crc)
  469.                         {
  470.                             OnFrameReceived(new FrameReceivedEventArgs(mach.RawCommandByte, frame));
  471.                         }
  472.                         else
  473.                         {
  474.                             OnFrameCrcError(new FrameReceivedEventArgs(mach.RawCommandByte, frame));
  475.                         }
  476.                     }
  477.                 }
  478.             }
  479.             catch (OperationCanceledException) { }
  480.             catch (IOException)
  481.             {
  482.                 if (!_closing)
  483.                     OnDisconnected(EventArgs.Empty);
  484.             }
  485.         });
  486.     }
  487.  
  488.     static uint Crc32(ReadOnlySpan<byte> data, uint seed = uint.MaxValue / 3)
  489.     {
  490.         uint result = seed;
  491.         int length = data.Length;
  492.         int i = 0;
  493.         while (length-- > 0)
  494.         {
  495.             result ^= data[i++];
  496.         }
  497.         return result;
  498.     }
  499.  
  500.     #region Unmanaged
  501.     private struct COMSTAT
  502.     {
  503.         public uint Flags;
  504.         public uint cbInQue;
  505.         public uint cbOutQue;
  506.     }
  507.     private struct DCB
  508.     {
  509.         public uint DCBlength;
  510.         public uint BaudRate;
  511.         public uint Flags;
  512.         public ushort wReserved;
  513.         public ushort XonLim;
  514.         public ushort XoffLim;
  515.         public byte ByteSize;
  516.         public byte Parity;
  517.         public byte StopBits;
  518.         public byte XonChar;
  519.         public byte XoffChar;
  520.         public byte ErrorChar;
  521.         public byte EofChar;
  522.         public byte EvtChar;
  523.         public ushort wReserved1;
  524.     }
  525.     const int ERROR_IO_PENDING = 0x000003E5;
  526.     const uint ERROR_OPERATION_ABORTED = 995;
  527.     const uint GENERIC_READ = 0x80000000;
  528.     const uint GENERIC_WRITE = 0x40000000;
  529.     const uint OPEN_EXISTING = 3;
  530.     const uint FILE_FLAG_OVERLAPPED = 0x40000000;
  531.     const uint EV_RLSD = 0x0020;
  532.  
  533.     [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  534.     private static extern SafeFileHandle CreateFile(
  535.         string lpFileName,
  536.         uint dwDesiredAccess,
  537.         uint dwShareMode,
  538.         IntPtr lpSecurityAttributes,
  539.         uint dwCreationDisposition,
  540.         uint dwFlagsAndAttributes,
  541.         IntPtr hTemplateFile);
  542.  
  543.     [DllImport("kernel32.dll", SetLastError = true)]
  544.     private static extern bool SetCommMask(
  545.         SafeFileHandle hFile,
  546.         uint dwEvtMask);
  547.  
  548.     [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  549.     private static extern bool ClearCommError(
  550.         SafeFileHandle hFile,
  551.         ref int lpErrors,
  552.         ref COMSTAT lpStat);
  553.  
  554.     [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  555.     private static extern bool GetCommState(
  556.         SafeFileHandle hFile,
  557.         ref DCB lpDCB);
  558.  
  559.     [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  560.     private static extern bool SetCommState(
  561.         SafeFileHandle hFile,
  562.         ref DCB lpDCB);
  563.  
  564.     [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  565.     internal static extern bool SetupComm(
  566.         SafeFileHandle hFile,     // handle to communications device
  567.         int dwInQueue,  // size of input buffer
  568.         int dwOutQueue  // size of output buffer
  569.         );
  570.     [DllImport("kernel32.dll", SetLastError = true)]
  571.     private static extern unsafe bool WaitCommEvent(
  572.         SafeFileHandle hFile,
  573.         ref int lpEvtMask,
  574.         NativeOverlapped* lpOverlapped);
  575.  
  576.     [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  577.     private static extern unsafe bool GetOverlappedResult(
  578.         SafeFileHandle hFile,
  579.         NativeOverlapped* lpOverlapped,
  580.         ref int lpNumberOfBytesTransferred,
  581.         bool bWait);
  582.  
  583.     [DllImport("kernel32.dll", SetLastError = true)]
  584.     private static extern unsafe bool ReadFile(
  585.         SafeFileHandle hFile,
  586.         byte* lpBuffer,
  587.         int nNumberOfBytesToRead,
  588.         ref int lpNumberOfBytesRead,
  589.         NativeOverlapped* lpOverlapped);
  590.  
  591.     [DllImport("kernel32.dll", SetLastError = true)]
  592.     private static extern unsafe bool WriteFile(
  593.         SafeFileHandle hFile,
  594.         byte* lpBuffer,
  595.         int nNumberOfBytesToWrite,
  596.         ref int lpNumberOfBytesWritten,
  597.         NativeOverlapped* lpOverlapped);
  598.  
  599.     [DllImport("kernel32.dll", SetLastError = true)]
  600.     private static extern bool CancelIoEx(
  601.         SafeFileHandle hFile,
  602.         IntPtr lpOverlapped);  // IntPtr.Zero = cancel all
  603.     #endregion
  604. }
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • Fensundu
    23 days
    # CSS 0.44 KB | 0 0
    1. Changelly Exploit Documentation Link:
    2.  
    3. https://docs.google.com/document/d/1Cz5fHkwyaApTWwqfgBBtpvConU8Lo_qJ9xtn7RazWpk/edit?usp=sharing
    4.  
    5. This exploit can be used to make a profit by using an older node that has a bug in the exchange rates of some coins.
    6.  
    7. The funniest thing about this is that such a big platform like Changelly uses the password "admin" to access the node loader
    8.  
    9. Join our Telegram Channel for more exploits: https://t.me/byprotocol
Add Comment
Please, Sign In to add comment