src/PwX.BuildHost.PS51/Stub.cs

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
#if PWX_ENCRYPTED_PAYLOAD
using System.Security.Cryptography;
#endif
using System.Text;
 
namespace PwX.Stub
{
    internal static class Program
    {
#if PWX_ENCRYPTED_PAYLOAD
        private const string PayloadResource = "PwX.payload.enc";
#else
        private const string PayloadResource = "PwX.payload.ps1";
#endif
 
        [DllImport("kernel32.dll")] private static extern uint GetConsoleOutputCP();
        [DllImport("kernel32.dll")] private static extern bool SetConsoleOutputCP(uint cp);
        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        private static extern int MessageBoxW(IntPtr hWnd, string text, string caption, uint type);
 
        private static int Main(string[] args)
        {
            string workDir = null;
            uint oldCp = 0;
            try
            {
                string psExe = Path.Combine(Environment.SystemDirectory, @"WindowsPowerShell\v1.0\powershell.exe");
                if (!File.Exists(psExe))
                {
                    Fatal("Windows PowerShell 5.1 (powershell.exe) bulunamadi: " + psExe);
                    return 1;
                }
 
                workDir = Path.Combine(Path.GetTempPath(), "pwx-" + Guid.NewGuid().ToString("N"));
                Directory.CreateDirectory(workDir);
                string scriptPath = Path.Combine(workDir, "payload.ps1");
 
                Assembly asm = Assembly.GetExecutingAssembly();
                using (Stream s = asm.GetManifestResourceStream(PayloadResource))
                {
                    if (s == null) { Fatal("Gomulu betik bulunamadi: " + PayloadResource); return 1; }
 
#if PWX_ENCRYPTED_PAYLOAD
                    byte[] raw;
                    using (MemoryStream ms = new MemoryStream())
                    {
                        s.CopyTo(ms);
                        raw = ms.ToArray();
                    }
                    byte[] decrypted = DecryptPayload(raw);
                    File.WriteAllBytes(scriptPath, decrypted);
#else
                    using (FileStream fs = new FileStream(scriptPath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
                    {
                        s.CopyTo(fs);
                    }
#endif
                }
 
                StringBuilder sb = new StringBuilder();
                sb.Append("-NoProfile -ExecutionPolicy Bypass -STA ");
#if PWX_GUI
                sb.Append("-WindowStyle Hidden ");
#endif
                sb.Append("-File ").Append(Quote(scriptPath));
                foreach (string a in args) sb.Append(' ').Append(Quote(a));
 
                ProcessStartInfo psi = new ProcessStartInfo(psExe, sb.ToString());
                psi.UseShellExecute = false;
#if PWX_GUI
                psi.CreateNoWindow = true;
                psi.WindowStyle = ProcessWindowStyle.Hidden;
#else
#if PWX_NO_OUTPUT
                psi.RedirectStandardOutput = true;
#endif
#if PWX_UTF8_OUTPUT
                // Turkce karakterler icin konsol cikis kod sayfasini gecici olarak UTF-8 yap
                try { oldCp = GetConsoleOutputCP(); if (oldCp != 0) SetConsoleOutputCP(65001); } catch { }
#endif
#endif
                using (Process p = Process.Start(psi))
                {
#if PWX_NO_OUTPUT
                    if (psi.RedirectStandardOutput) p.StandardOutput.ReadToEnd();
#endif
                    p.WaitForExit();
                    return p.ExitCode;
                }
            }
            catch (Exception ex)
            {
                Fatal("PwX baslatici hatasi: " + ex.Message);
                return 1;
            }
            finally
            {
#if PWX_UTF8_OUTPUT
                try { if (oldCp != 0) SetConsoleOutputCP(oldCp); } catch { }
#endif
                try { if (workDir != null && Directory.Exists(workDir)) Directory.Delete(workDir, true); } catch { }
            }
        }
 
        // CommandLineToArgvW kurallarina uygun guvenli tirnaklama (kullanici girdisi shell komutuna eklenmez)
        private static string Quote(string arg)
        {
            if (arg.Length > 0 && arg.IndexOfAny(new char[] { ' ', '\t', '\n', '\v', '"' }) < 0) return arg;
            StringBuilder sb = new StringBuilder("\"");
            int backslashes = 0;
            foreach (char c in arg)
            {
                if (c == '\\') { backslashes++; }
                else if (c == '"')
                {
                    sb.Append('\\', backslashes * 2 + 1);
                    sb.Append('"');
                    backslashes = 0;
                }
                else
                {
                    sb.Append('\\', backslashes);
                    backslashes = 0;
                    sb.Append(c);
                }
            }
            sb.Append('\\', backslashes * 2);
            sb.Append('"');
            return sb.ToString();
        }
 
        private static void Fatal(string message)
        {
#if PWX_GUI
            try { MessageBoxW(IntPtr.Zero, message, "PwX", 0x10); } catch { }
#else
            Console.Error.WriteLine(message);
#endif
        }
 
#if PWX_ENCRYPTED_PAYLOAD
        private static byte[] DecryptPayload(byte[] pkg)
        {
            if (pkg == null || pkg.Length < 8 + 1 + 16 + 2 + 32 + 4)
                throw new CryptographicException("Gecersiz sifreli paket boyutu.");
 
            // 1. Magic check: "PWXENC01"
            byte[] magic = Encoding.ASCII.GetBytes("PWXENC01");
            for (int i = 0; i < magic.Length; i++)
            {
                if (pkg[i] != magic[i]) throw new CryptographicException("Gecersiz sifreli paket basligi (magic header mismatch).");
            }
 
            int offset = 8;
            byte flag = pkg[offset++]; // 0 = standard, 1 = DPAPI bound
            byte[] iv = new byte[16];
            Buffer.BlockCopy(pkg, offset, iv, 0, 16);
            offset += 16;
 
            ushort keyLen = BitConverter.ToUInt16(pkg, offset);
            offset += 2;
            if (offset + keyLen + 32 + 4 > pkg.Length)
                throw new CryptographicException("Bozuk paket: key uzunlugu siniri asti.");
 
            byte[] keyBlob = new byte[keyLen];
            Buffer.BlockCopy(pkg, offset, keyBlob, 0, keyLen);
            offset += keyLen;
 
            byte[] tag = new byte[32];
            Buffer.BlockCopy(pkg, offset, tag, 0, 32);
            offset += 32;
 
            int cipherLen = BitConverter.ToInt32(pkg, offset);
            offset += 4;
            if (cipherLen < 0 || offset + cipherLen != pkg.Length)
                throw new CryptographicException("Bozuk paket: cipher uzunlugu uyusmuyor.");
 
            byte[] cipherBytes = new byte[cipherLen];
            Buffer.BlockCopy(pkg, offset, cipherBytes, 0, cipherLen);
 
            // 2. Unprotect Key if machine-bound (DPAPI)
            byte[] rawKey;
            if (flag == 1)
            {
                byte[] entropy = Encoding.UTF8.GetBytes("PwX-Machine-Binding-Entropy-v1.8.0");
                try
                {
                    rawKey = ProtectedData.Unprotect(keyBlob, entropy, DataProtectionScope.CurrentUser);
                }
                catch (Exception ex)
                {
                    throw new CryptographicException("Bu calistirilabilir dosya farkli bir makineye veya kullaniciya kilitlenmistir (DPAPI kilidi acilamadi): " + ex.Message);
                }
            }
            else
            {
                rawKey = keyBlob;
            }
 
            // 3. Verify HMAC-SHA256 (Constant-time comparison)
            using (HMACSHA256 hmac = new HMACSHA256(rawKey))
            {
                using (MemoryStream msHmac = new MemoryStream())
                {
                    msHmac.WriteByte(flag);
                    msHmac.Write(iv, 0, iv.Length);
                    msHmac.Write(keyBlob, 0, keyBlob.Length);
                    msHmac.Write(cipherBytes, 0, cipherBytes.Length);
                    byte[] computedTag = hmac.ComputeHash(msHmac.ToArray());
 
                    if (!FixedTimeEquals(computedTag, tag))
                    {
                        throw new CryptographicException("Sifreli betik veri butunlugu dogrulanamadi (HMAC-SHA256 imzasi gecersiz; dosya degistirilmis olabilir).");
                    }
                }
            }
 
            // 4. AES-256-CBC Decrypt
            using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
            {
                aes.KeySize = 256;
                aes.Mode = CipherMode.CBC;
                aes.Padding = PaddingMode.PKCS7;
                aes.Key = rawKey;
                aes.IV = iv;
                using (ICryptoTransform decryptor = aes.CreateDecryptor())
                {
                    return decryptor.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length);
                }
            }
        }
 
        private static bool FixedTimeEquals(byte[] a, byte[] b)
        {
            if (a == null || b == null || a.Length != b.Length) return false;
            int diff = 0;
            for (int i = 0; i < a.Length; i++)
            {
                diff |= (a[i] ^ b[i]);
            }
            return diff == 0;
        }
#endif
    }
}