param( [string]$Package = "com.kuaishou.nebula", [string]$Script = "out\probe_nebula_all_layers.js", [string]$OutDir = "out", [int]$PollSeconds = 90 ) $ErrorActionPreference = "Stop" function Get-AppProcesses { param([string]$Pkg) $found = @{} try { $rows = & frida-ps -U 2>$null | Select-String $Pkg foreach ($row in $rows) { $line = $row.Line.Trim() if ($line -match "^(\d+)\s+(.+)$") { $pidValue = [int]$matches[1] $nameValue = $matches[2].Trim() $found[$pidValue] = [pscustomobject]@{ Pid = $pidValue; Name = $nameValue; Source = "frida-ps" } } } } catch {} try { $rows = & adb shell ps -A 2>$null | Select-String $Pkg foreach ($row in $rows) { $line = $row.Line.Trim() $parts = @($line -split "\s+") if ($parts.Length -ge 2 -and $parts[1] -match "^\d+$") { $pidValue = [int]$parts[1] $nameValue = $parts[$parts.Length - 1] $found[$pidValue] = [pscustomobject]@{ Pid = $pidValue; Name = $nameValue; Source = "adb-ps" } } } } catch {} foreach ($item in $found.Values) { $item } } function Safe-Name { param([string]$Name) return (($Name -replace '[^A-Za-z0-9_.-]+', '_').Trim('_')) } if (!(Test-Path $Script)) { throw "Frida script not found: $Script" } if (!(Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null } $Root = (Get-Location).ProviderPath $ScriptPath = (Resolve-Path $Script).ProviderPath $OutDirPath = (Resolve-Path $OutDir).ProviderPath $ts = Get-Date -Format yyyyMMdd_HHmmss $seen = @{} $jobs = @() $deadline = (Get-Date).AddSeconds($PollSeconds) Write-Host ("MULTI_ATTACH package={0} script={1} poll={2}s ts={3}" -f $Package, $ScriptPath, $PollSeconds, $ts) Write-Host "Open the main UI first, then run the login flow. This script polls child processes." while ((Get-Date) -lt $deadline) { $procs = @(Get-AppProcesses -Pkg $Package) foreach ($p in $procs) { if ($seen.ContainsKey($p.Pid)) { continue } $seen[$p.Pid] = $true $safe = Safe-Name $p.Name $log = Join-Path $OutDirPath ("probe_multi_{0}_{1}_{2}.log" -f $ts, $p.Pid, $safe) Write-Host ("ATTACH pid={0} name={1} source={2} log={3}" -f $p.Pid, $p.Name, $p.Source, $log) $jobs += Start-Job -Name ("frida_{0}" -f $p.Pid) -ArgumentList $p.Pid,$p.Name,$ScriptPath,$log,$Root -ScriptBlock { param($PidValue, $ProcessName, $ScriptAbsPath, $LogPath, $WorkDir) try { Set-Location -LiteralPath $WorkDir ("JOB_BEGIN pid={0} name={1} script={2} ts={3}" -f $PidValue, $ProcessName, $ScriptAbsPath, (Get-Date -Format o)) | Out-File -Encoding UTF8 -FilePath $LogPath & frida -U -p $PidValue -l $ScriptAbsPath 2>&1 | Tee-Object -FilePath $LogPath -Append ("JOB_EXIT pid={0} exit={1} ts={2}" -f $PidValue, $LASTEXITCODE, (Get-Date -Format o)) | Add-Content -Encoding UTF8 -Path $LogPath } catch { ("JOB_ERR pid={0} err={1} ts={2}" -f $PidValue, $_, (Get-Date -Format o)) | Add-Content -Encoding UTF8 -Path $LogPath } } } Start-Sleep -Seconds 2 } Write-Host ("POLL_DONE attached_count={0}" -f $seen.Count) Write-Host "After login: Get-Job | Stop-Job; Get-Job | Remove-Job" Write-Host "View one: Receive-Job -Keep -Name frida_" if ($PollSeconds -le 0) { return } while ($true) { Start-Sleep -Seconds 5 $running = @($jobs | Where-Object { $_.State -eq 'Running' }) Write-Host ("JOB_STATUS running={0} total={1}" -f $running.Count, $jobs.Count) }