Ctrl K
ComunidadGuías…

_widevine_setup/wvd_console.ps1

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# ============================================================================
#  Widevine L3 Console - one place for the whole device.wvd pipeline.
#  Launch:  wvd.bat   (or:  powershell -ExecutionPolicy Bypass -File wvd_console.ps1)
#
#  Method that WORKS: Android Studio AVD (google_apis x86_64 ships real Widevine
#  L3) + KeyDive. MEmu/LDPlayer ship ClearKey-only - dead end, removed.
# ============================================================================
param([switch]$Auto)   # -Auto: run the whole pipeline headless (no menu, no prompts)
$ErrorActionPreference = "SilentlyContinue"
$HERE   = Split-Path -Parent $MyInvocation.MyCommand.Path
$ROOT   = Split-Path -Parent $HERE                       # C:\dev\apple_music
$SDK    = "C:\Android\Sdk"
# JRE: pick the jdk-* dir dynamically (don't hardcode 17.0.x - it changes).
$JRE    = (Get-ChildItem "C:\Android\jre17\jdk-*" -Directory -EA SilentlyContinue | Select-Object -First 1).FullName
if(-not $JRE){ $JRE = "C:\Android\jre17" }
$ADB    = "$SDK\platform-tools\adb.exe"
$EMU    = "$SDK\emulator\emulator.exe"
$SERIAL = "emulator-5554"
$AVD    = "wvd"
$IMAGE  = "system-images;android-30;google_apis;x86_64"
$SDKM   = "$SDK\cmdline-tools\latest\bin\sdkmanager.bat"
$AVDM   = "$SDK\cmdline-tools\latest\bin\avdmanager.bat"
$FRIDA  = "$HERE\frida-server-x86_64"
# Interpreter: dev .venv OR the bundled embeddable python\ that ships in the
# installer. The old code only knew .venv -> on a real install keydive.exe was
# never found and Extract silently produced no .wvd.
if(Test-Path "$ROOT\.venv\Scripts\python.exe"){ $VENVPY = "$ROOT\.venv\Scripts\python.exe" } elseif(Test-Path "$ROOT\python\python.exe"){ $VENVPY = "$ROOT\python\python.exe" } else { $VENVPY = "python" }
$KEYDIVE= Join-Path (Split-Path -Parent $VENVPY) "Scripts\keydive.exe"
$WVDDST = "$ROOT\tools\widevine\device.wvd"
$env:JAVA_HOME = $JRE
$env:ANDROID_SDK_ROOT = $SDK
$env:PATH = "$JRE\bin;$SDK\platform-tools;$SDK\emulator;$env:PATH"

function Mark($ok){ if($ok){"[OK]"}else{"[ -- ]"} }
function Line($label,$ok,$detail){ "{0,-6} {1,-22} {2}" -f (Mark $ok),$label,$detail }

function Show-Status {
    Write-Host "`n==== Widevine L3 pipeline status ====" -ForegroundColor Cyan
    Line "JRE 17"        (Test-Path "$JRE\bin\java.exe")                 $JRE
    Line "platform-tools"(Test-Path $ADB)                               "adb"
    Line "emulator"      (Test-Path $EMU)                               "emulator.exe"
    Line "system-image"  (Test-Path "$SDK\system-images\android-30\google_apis\x86_64\system.img") "android-30 google_apis x86_64"
    $aehd = (sc.exe query aehd 2>$null | Select-String "RUNNING") -ne $null
    Line "AEHD driver"   $aehd                                          "hypervisor (needs admin to install)"
    Line "AVD '$AVD'"    (Test-Path "$env:USERPROFILE\.android\avd\$AVD.avd") ""
    $emuUp = (Get-Process qemu-system-x86_64 -EA SilentlyContinue) -ne $null
    Line "emulator up"   $emuUp                                         $SERIAL
    Line "device.wvd"    (Test-Path $WVDDST)                            $WVDDST
    Write-Host ""
}

function Connect-Wifi {
    # THE fix that unblocked everything (Jun 2026): on a cold boot the guest has
    # NO saved Wi-Fi network, so wlan0 never associates with the emulator's
    # simulated AP "AndroidWifi". Result: ConnectivityService reports
    # "Active default network: none" -> Chrome has no internet -> KeyDive's DRM
    # playback can't do a license exchange -> no .wvd. One command associates it.
    # (eth0 NAT is alive at the qemu layer but the image ships no EthernetService,
    #  so Wi-Fi via netsim is the only path the framework will adopt.)
    Write-Host "Connecting guest Wi-Fi to AndroidWifi..." -ForegroundColor Cyan
    & $ADB -s $SERIAL shell "cmd wifi connect-network AndroidWifi open" 2>$null | Out-Null
    for($i=0;$i -lt 15;$i++){
        $net = (& $ADB -s $SERIAL shell "dumpsys connectivity | grep 'Active default network'" 2>$null)
        if($net -and ($net -notmatch "none")){ Write-Host "  Network up: $($net.Trim())" -ForegroundColor Green; return $true }
        Start-Sleep 2
    }
    Write-Host "  WARN: emulator network still 'none' after Wi-Fi connect." -ForegroundColor Yellow
    # A bare fact gets scrolled past. The consequence is what matters here:
    # with no guest network KeyDive cannot do a license exchange, so minting
    # WILL fail - and downstream that looks like "it froze for no reason".
    Write-Host "  This is fatal for minting: KeyDive needs the guest online to do a" -ForegroundColor Yellow
    Write-Host "  license exchange. Expect the extraction step to fail." -ForegroundColor Yellow
    Write-Host "  Usual causes: AEHD/hypervisor not running, or the emulator booted" -ForegroundColor Yellow
    Write-Host "  before netsim was ready - stop the emulator (5) and retry." -ForegroundColor Yellow
    return $false
}

function Boot-Emulator {
    if(-not (Get-Process qemu-system-x86_64 -EA SilentlyContinue)){
        if(-not ((sc.exe query aehd 2>$null | Select-String "RUNNING"))){
            Write-Host "!! AEHD hypervisor not running - x86_64 emulator can't boot." -ForegroundColor Red
            Write-Host "   Install it (one-time, needs admin/UAC):" -ForegroundColor Red
            Write-Host "   $SDK\extras\google\Android_Emulator_Hypervisor_Driver\silent_install.bat" -ForegroundColor Red
            return $false
        }
        # -no-window: NEVER pop the phone UI on a tester's desktop (mints headless).
        Write-Host "Launching emulator (headless, no window)..." -ForegroundColor Cyan
        Start-Process -FilePath $EMU -WindowStyle Hidden -ArgumentList `
            "-avd",$AVD,"-no-window","-no-snapshot","-no-boot-anim","-gpu","swiftshader_indirect","-no-audio",`
            "-dns-server","8.8.8.8,8.8.4.4","-netdelay","none","-netspeed","full"
        & $ADB start-server | Out-Null
        Write-Host "Waiting for boot..." -NoNewline
        for($i=0;$i -lt 30;$i++){
            Start-Sleep 5
            if((& $ADB -s $SERIAL shell getprop sys.boot_completed 2>$null).Trim() -eq "1"){ Write-Host " booted." -ForegroundColor Green; break }
            Write-Host "." -NoNewline
        }
    } else { Write-Host "Emulator already running." -ForegroundColor Yellow }
    & $ADB -s $SERIAL root | Out-Null
    Start-Sleep 2
    Connect-Wifi | Out-Null    # <-- network fix; must run BEFORE KeyDive (every boot)
    Ensure-Frida               # <-- now runs even if the emulator was already up
    return $true
}

function Ensure-Frida {
    # CRITICAL: the frida-server on the device MUST match KeyDive's frida CLIENT
    # version, else KeyDive reports "Frida server is not running" even though it is.
    # Derive the version from the installed frida and fetch the matching android
    # x86_64 server (cached). The bundled frida-server-x86_64 is only a last resort.
    $ver = (& $VENVPY -c "import frida; print(frida.__version__)" 2>$null)
    if($ver){ $ver = $ver.Trim() }
    $srv = $FRIDA
    if($ver){
        $cached = "$HERE\frida-server-$ver-x86_64"
        if(-not (Test-Path $cached)){
            $url = "https://github.com/frida/frida/releases/download/$ver/frida-server-$ver-android-x86_64.xz"
            $xz  = "$cached.xz"
            Write-Host "Downloading frida-server $ver (matches KeyDive)..." -ForegroundColor Cyan
            # -TimeoutSec bounds the REQUEST, not the body transfer. On Windows
            # PowerShell 5.1 a stalled GitHub download can sit for hours with one
            # motionless "Downloading..." line on screen. That is exactly where
            # "one-click install does nothing" came from (22.08.2026): it is
            # indistinguishable from a hang, and the bundled fallback lives in
            # the catch block, which a stall NEVER reaches.
            #
            # So: download in a background job and bound the WHOLE step by wall
            # clock. If it does not finish, kill the job and fall back honestly
            # instead of waiting forever.
            $dlOk = $false
            $deadline = 150
            try {
                $job = Start-Job -ScriptBlock {
                    param($u, $o)
                    $ProgressPreference = 'SilentlyContinue'
                    Invoke-WebRequest -UseBasicParsing -Uri $u -OutFile $o -TimeoutSec 60
                } -ArgumentList $url, $xz
                # A heartbeat every 10s. A silent screen reads as "frozen" and
                # people kill the window even when the work is going fine.
                $waited = 0
                while($waited -lt $deadline -and $job.State -eq 'Running'){
                    Start-Sleep -Seconds 5; $waited += 5
                    if($waited % 10 -eq 0){
                        $sz = 0
                        if(Test-Path $xz){ $sz = [math]::Round((Get-Item $xz).Length / 1MB, 1) }
                        Write-Host ("  ...{0}s, {1} MB" -f $waited, $sz) -ForegroundColor DarkGray
                    }
                }
                if($job.State -eq 'Running'){
                    Stop-Job $job -EA SilentlyContinue
                    Write-Host "  frida-server: no progress in ${deadline}s - giving up on the download." -ForegroundColor Yellow
                } else {
                    Receive-Job $job -EA SilentlyContinue | Out-Null
                    if((Test-Path $xz) -and (Get-Item $xz).Length -gt 0){ $dlOk = $true }
                }
                Remove-Job $job -Force -EA SilentlyContinue
                if($dlOk){
                    & $VENVPY -c "import lzma,sys; open(sys.argv[2],'wb').write(lzma.open(sys.argv[1]).read())" $xz $cached
                }
                Remove-Item $xz -Force -EA SilentlyContinue
            } catch { Write-Host "frida-server $ver download failed ($_) - using bundled." -ForegroundColor Yellow }
            if(-not $dlOk){
                # Name the CONSEQUENCE, not just the fact. A frida version
                # mismatch surfaces in KeyDive as "Frida server is not running",
                # a message that leads nobody to the real cause.
                Write-Host "  Falling back to the bundled frida-server." -ForegroundColor Yellow
                Write-Host "  If KeyDive later says 'Frida server is not running', the cause is a" -ForegroundColor Yellow
                Write-Host "  version mismatch with client $ver, not a dead server." -ForegroundColor Yellow
            }
        }
        if(Test-Path $cached){ $srv = $cached }
    }
    # Kill any stale (possibly wrong-version) frida-server already on the device.
    & $ADB -s $SERIAL shell "pkill -f frida-server" 2>$null | Out-Null
    Write-Host "Pushing frida-server to device..." -ForegroundColor Cyan
    & $ADB -s $SERIAL push "$srv" /data/local/tmp/frida-server 2>$null | Out-Null
    & $ADB -s $SERIAL shell "chmod 755 /data/local/tmp/frida-server" 2>$null | Out-Null
    & $ADB -s $SERIAL shell "nohup /data/local/tmp/frida-server >/data/local/tmp/fs.log 2>&1 &" | Out-Null
    Start-Sleep 3
    $running = & $ADB -s $SERIAL shell "ps -A 2>/dev/null | grep frida-server" 2>$null
    if($running){ Write-Host "frida-server $ver started." -ForegroundColor Green }
    else {
        $log = & $ADB -s $SERIAL shell "cat /data/local/tmp/fs.log 2>/dev/null"
        Write-Host "frida-server did NOT start. Log: $log" -ForegroundColor Red
    }
}

function Skip-ChromeFRE {
    # KeyDive's -a web drives Chrome; a fresh profile stops on the welcome/First-Run
    # screen and KeyDive hangs (the classic "Chrome stuck on welcome" snag). With adb
    # root we pre-seed the chrome-command-line flag file so Chrome skips the FRE - best
    # effort, never fatal.
    & $ADB -s $SERIAL shell "echo 'chrome --no-first-run --disable-fre --no-default-browser-check' > /data/local/tmp/chrome-command-line" 2>$null | Out-Null
    & $ADB -s $SERIAL shell "chmod 644 /data/local/tmp/chrome-command-line" 2>$null | Out-Null
    # Best-effort: mark Play-services/Chrome first-run done so no setup wizard steals focus.
    & $ADB -s $SERIAL shell "settings put global device_provisioned 1" 2>$null | Out-Null
    & $ADB -s $SERIAL shell "settings put secure user_setup_complete 1" 2>$null | Out-Null
}

function Ensure-KeyDive {
    # KeyDive is a pip package; a bundled install has python\ but not keydive yet.
    # Install it into whichever interpreter we resolved so Extract never fails with
    # a silent "no .wvd" just because the tool was missing.
    & $VENVPY -c "import keydive" 2>$null
    if($LASTEXITCODE -eq 0){ return $true }
    Write-Host "Installing KeyDive (pip)..." -ForegroundColor Cyan
    & $VENVPY -m pip install --upgrade --disable-pip-version-check keydive
    & $VENVPY -c "import keydive" 2>$null
    return ($LASTEXITCODE -eq 0)
}

function Ensure-Sdk {
    # platform-tools + emulator + system-image + the AEHD driver package. sdkmanager
    # resumes partial downloads; runs elevated when Ripster itself is admin.
    if((Test-Path $ADB) -and (Test-Path "$SDK\system-images\android-30\google_apis\x86_64\system.img")){ return $true }
    if(-not (Test-Path $SDKM)){ Write-Host "!! cmdline-tools missing - run the in-app Setup (JRE + SDK tools) first." -ForegroundColor Red; return $false }
    Write-Host "Installing SDK components (platform-tools, emulator, system-image, AEHD)..." -ForegroundColor Cyan
    cmd /c "echo y| `"$SDKM`" `"platform-tools`" `"emulator`" `"$IMAGE`" `"extras;google;Android_Emulator_Hypervisor_Driver`""
    return ((Test-Path $ADB) -and (Test-Path "$SDK\system-images\android-30\google_apis\x86_64\system.img"))
}

function Ensure-Aehd {
    # Hypervisor driver — the one admin-gated step. With Ripster running elevated
    # (PrivilegesRequired=admin / uac_admin) this installs without a separate UAC.
    if((sc.exe query aehd 2>$null | Select-String "RUNNING")){ return $true }
    $inst = "$SDK\extras\google\Android_Emulator_Hypervisor_Driver\silent_install.bat"
    if(-not (Test-Path $inst)){ Write-Host "AEHD installer missing - run Ensure-Sdk first." -ForegroundColor Red; return $false }
    Write-Host "Installing AEHD hypervisor driver..." -ForegroundColor Cyan
    Push-Location (Split-Path $inst); cmd /c "`"$inst`""; Pop-Location
    return ((sc.exe query aehd 2>$null | Select-String "RUNNING") -ne $null)
}

function Ensure-Avd {
    if(Test-Path "$env:USERPROFILE\.android\avd\$AVD.avd"){ return $true }
    if(-not (Test-Path $AVDM)){ Write-Host "avdmanager missing - run the in-app Setup first." -ForegroundColor Red; return $false }
    Write-Host "Creating AVD '$AVD'..." -ForegroundColor Cyan
    cmd /c "echo no| `"$AVDM`" create avd -n $AVD -k `"$IMAGE`" --force"
    return (Test-Path "$env:USERPROFILE\.android\avd\$AVD.avd")
}

function One-Click {
    # The whole pipeline in one go: provision everything that can be auto-installed,
    # then boot -> extract -> verify. Assumes admin (Ripster runs elevated).
    if(Test-Path $WVDDST){ Write-Host "device.wvd already present: $WVDDST" -ForegroundColor Green; return }
    if(-not (Ensure-Sdk)){  Write-Host "ONE_CLICK: FAIL sdk"  -ForegroundColor Red; return }
    if(-not (Ensure-Aehd)){ Write-Host "ONE_CLICK: FAIL aehd (needs admin)" -ForegroundColor Red; return }
    if(-not (Ensure-Avd)){  Write-Host "ONE_CLICK: FAIL avd"  -ForegroundColor Red; return }
    Extract-Wvd                                  # boot -> KeyDive -> auto Install-Wvd
    if(Test-Path $WVDDST){ Verify-Wvd; Write-Host "ONE_CLICK: OK $WVDDST" -ForegroundColor Green }
    else { Write-Host "ONE_CLICK: FAIL extract (KeyDive produced no .wvd)" -ForegroundColor Red }
}

function Extract-Wvd {
    if(-not (Boot-Emulator)){ return }
    if(-not (Ensure-KeyDive)){ Write-Host "KeyDive install failed (pip) - check your internet." -ForegroundColor Red; return }
    Skip-ChromeFRE
    Write-Host "Running KeyDive (-a web -> Chrome plays DRM -> captures CDM)..." -ForegroundColor Cyan
    if(Test-Path "$HERE\_keydive_out"){ Remove-Item "$HERE\_keydive_out" -Recurse -Force }
    Push-Location $HERE
    # Invoke via -m (NOT the .exe shim - console-script .exe shims don't run under
    # the embeddable python; -m keydive works on every layout).
    & $VENVPY -m keydive -s $SERIAL --output "_keydive_out" -w -a web
    Pop-Location
    $wvd = Get-ChildItem "$HERE\_keydive_out" -Recurse -Filter *.wvd | Select-Object -First 1
    if($wvd){ Write-Host "Extracted: $($wvd.FullName)" -ForegroundColor Green; Install-Wvd }
    else { Write-Host "No .wvd produced. If Chrome stuck on welcome screen, accept it and retry." -ForegroundColor Red }
}

function Install-Wvd {
    $wvd = Get-ChildItem "$HERE\_keydive_out" -Recurse -Filter *.wvd | Select-Object -First 1
    if(-not $wvd){ Write-Host "No extracted .wvd to install - run Extract first." -ForegroundColor Red; return }
    New-Item -ItemType Directory -Force "$ROOT\tools\widevine" | Out-Null
    Copy-Item $wvd.FullName $WVDDST -Force
    Write-Host "Installed -> $WVDDST" -ForegroundColor Green
}

function Verify-Wvd {
    if(-not (Test-Path $WVDDST)){ Write-Host "No device.wvd installed." -ForegroundColor Red; return }
    Write-Host "Verifying against SoundCloud Widevine license server..." -ForegroundColor Cyan
    & $VENVPY "$HERE\verify_wvd.py"
}

function Stop-Emulator {
    & $ADB -s $SERIAL emu kill 2>$null | Out-Null
    Get-Process qemu-system-x86_64 -EA SilentlyContinue | Stop-Process -Force
    Write-Host "Emulator stopped." -ForegroundColor Green
}

# ---- AUTO mode: full pipeline headless, no prompts (driven by the Setup tab) ----
if($Auto){
    try { Start-Transcript -Path "$HERE\wvd_mint.log" -Append -EA SilentlyContinue | Out-Null } catch {}
    Write-Host ("=== AUTO MINT run: {0} ===" -f (Get-Date)) -ForegroundColor DarkCyan
    Show-Status
    if(Test-Path $WVDDST){ Write-Host "AUTO_RESULT: OK (device.wvd already present) $WVDDST" -ForegroundColor Green; exit 0 }
    Write-Host "=== AUTO MINT: provision -> boot -> extract -> install -> verify -> stop ===" -ForegroundColor Cyan
    if(-not (Ensure-Sdk)){  Write-Host "AUTO_RESULT: FAIL sdk"  -ForegroundColor Red; exit 2 }
    if(-not (Ensure-Aehd)){ Write-Host "AUTO_RESULT: FAIL aehd (needs admin)" -ForegroundColor Red; exit 2 }
    if(-not (Ensure-Avd)){  Write-Host "AUTO_RESULT: FAIL avd"  -ForegroundColor Red; exit 2 }
    if(-not (Boot-Emulator)){ Write-Host "AUTO_RESULT: FAIL boot (AEHD/emulator)" -ForegroundColor Red; exit 2 }
    Extract-Wvd                              # KeyDive -> auto Install-Wvd
    if(-not (Test-Path $WVDDST)){
        Write-Host "AUTO_RESULT: FAIL extract (KeyDive produced no .wvd - Chrome FRE/DRM?)" -ForegroundColor Red
        Stop-Emulator; exit 3
    }
    Verify-Wvd
    Stop-Emulator
    Write-Host "AUTO_RESULT: OK $WVDDST" -ForegroundColor Green
    exit 0
}

# ---- menu loop ----
while($true){
    Show-Status
    Write-Host "  [9] ONE-CLICK: install everything + extract device.wvd" -ForegroundColor Green
    Write-Host "  [1] Boot emulator + frida-server"
    Write-Host "  [2] Extract device.wvd (KeyDive)  -> auto-installs"
    Write-Host "  [3] Install last extracted .wvd into Ripster"
    Write-Host "  [4] Verify .wvd against SoundCloud"
    Write-Host "  [5] Stop emulator"
    Write-Host "  [0] Exit"
    $c = Read-Host "`nChoose"
    switch($c){
        "9"{ One-Click }
        "1"{ Boot-Emulator }
        "2"{ Extract-Wvd }
        "3"{ Install-Wvd }
        "4"{ Verify-Wvd }
        "5"{ Stop-Emulator }
        "0"{ break }
        default{ Write-Host "?" -ForegroundColor Yellow }
    }
}