ZaStoGram/Tools/collect_mtproxy_logs.ps1
loop-uh cc9c15c641 Add transport invariant checks and state helpers
Refactor ConnectionSocket to centralize transport/proxy/tls state handling and validations. Introduces allowed-state transition checks, named state helpers (setTransportState, setProxyAuthState, setTlsState), many can*/check* guard functions, action rules for transport actions, and setters that log state changes (socket/epoll/admission/timers). Replaces many direct boolean assignments with these helpers, adds runtime invariant logging, and tightens checks around sending TLS/ClientHello and mtproxy endpoint data-path (validates accepted reasons). Documentation updated to mention mtproxy_runtime_contract and the new verify_mtproxy_runtime_logs.py; several Tools scripts added/updated to support runtime log contract verification.
2026-06-24 13:19:09 +03:00

290 lines
12 KiB
PowerShell

param(
[string] $Package = "org.zastogram.messenger",
[string] $OutDir = "",
[string] $Adb = "",
[string] $Serial = "",
[int] $Seconds = 0,
[switch] $NoClear,
[switch] $PullOnly
)
$ErrorActionPreference = "Stop"
function Resolve-AdbPath {
$candidates = @()
if (-not [string]::IsNullOrWhiteSpace($Adb)) {
$candidates += $Adb
}
if (-not [string]::IsNullOrWhiteSpace($env:ANDROID_HOME)) {
$candidates += (Join-Path $env:ANDROID_HOME "platform-tools\adb.exe")
}
if (-not [string]::IsNullOrWhiteSpace($env:ANDROID_SDK_ROOT)) {
$candidates += (Join-Path $env:ANDROID_SDK_ROOT "platform-tools\adb.exe")
}
if (-not [string]::IsNullOrWhiteSpace($env:LOCALAPPDATA)) {
$candidates += (Join-Path $env:LOCALAPPDATA "Android\Sdk\platform-tools\adb.exe")
}
$pathAdb = Get-Command adb.exe -ErrorAction SilentlyContinue
if ($pathAdb) {
$candidates += $pathAdb.Source
}
foreach ($candidate in $candidates) {
if (-not [string]::IsNullOrWhiteSpace($candidate) -and (Test-Path $candidate)) {
return (Resolve-Path $candidate).Path
}
}
throw "adb.exe not found. Pass -Adb C:\path\to\adb.exe or install Android SDK platform-tools."
}
$script:AdbExe = Resolve-AdbPath
$script:AdbPrefix = @()
if (-not [string]::IsNullOrWhiteSpace($Serial)) {
$script:AdbPrefix += @("-s", $Serial)
}
function Invoke-Adb {
param([Parameter(ValueFromRemainingArguments = $true)][string[]] $Args)
$allArgs = @()
$allArgs += $script:AdbPrefix
$allArgs += $Args
& $script:AdbExe @allArgs
}
function Test-DevicePackage {
param([string] $Name)
if ([string]::IsNullOrWhiteSpace($Name)) {
return $false
}
$result = Invoke-Adb shell pm path $Name 2>$null
return ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace(($result | Out-String).Trim()))
}
function Resolve-DevicePackage {
param([string] $RequestedPackage)
if (Test-DevicePackage $RequestedPackage) {
return $RequestedPackage
}
$knownPackages = @(
"org.zastogram.messenger.beta",
"org.zastogram.messenger",
"org.zastogram.messenger.web",
"org.telegram.messenger.beta",
"org.telegram.messenger",
"org.telegram.messenger.web"
)
foreach ($knownPackage in $knownPackages) {
if ($knownPackage -ne $RequestedPackage -and (Test-DevicePackage $knownPackage)) {
Write-Warning "Package '$RequestedPackage' was not found; using '$knownPackage'. Pass -Package to override."
return $knownPackage
}
}
Write-Warning "Package '$RequestedPackage' was not found. Continuing anyway; file-log pull may fail."
return $RequestedPackage
}
if ([string]::IsNullOrWhiteSpace($OutDir)) {
$OutDir = Join-Path (Get-Location) "mtproxy-logs"
}
$stamp = Get-Date -Format "yyyyMMdd-HHmmss"
$sessionDir = Join-Path $OutDir $stamp
New-Item -ItemType Directory -Force -Path $sessionDir | Out-Null
$resolvedPackage = Resolve-DevicePackage $Package
$metaPath = Join-Path $sessionDir "meta.txt"
"adb=$script:AdbExe" | Set-Content -Encoding UTF8 $metaPath
"serial=$Serial" | Add-Content -Encoding UTF8 $metaPath
"package=$resolvedPackage" | Add-Content -Encoding UTF8 $metaPath
"started=$((Get-Date).ToString('o'))" | Add-Content -Encoding UTF8 $metaPath
Invoke-Adb devices -l | Tee-Object -FilePath (Join-Path $sessionDir "adb_devices.txt") | Out-Host
Invoke-Adb shell pm path $resolvedPackage 2>&1 | Tee-Object -FilePath (Join-Path $sessionDir "pm_path.txt") | Out-Null
Invoke-Adb shell dumpsys package $resolvedPackage 2>&1 | Set-Content -Encoding UTF8 (Join-Path $sessionDir "dumpsys_package.txt")
if (-not $PullOnly) {
if (-not $NoClear) {
Invoke-Adb logcat -c | Out-Null
}
$logcatPath = Join-Path $sessionDir "logcat.txt"
$logcatErrPath = Join-Path $sessionDir "logcat.stderr.txt"
$logcatArgs = @()
$logcatArgs += $script:AdbPrefix
$logcatArgs += @(
"logcat",
"-v",
"threadtime",
"-s",
"tgnet:V",
"tgnetREF:V",
"tmessages:V",
"MTProto:V",
"AndroidRuntime:E",
"*:S"
)
Write-Host ""
Write-Host "Recording logcat to $logcatPath"
if ($Seconds -gt 0) {
Write-Host "Run the MTProxy test now. Recording will stop in $Seconds seconds."
} else {
Write-Host "Run the MTProxy test now, then press Enter here to stop recording."
}
$process = Start-Process -FilePath $script:AdbExe -ArgumentList $logcatArgs -RedirectStandardOutput $logcatPath -RedirectStandardError $logcatErrPath -PassThru -WindowStyle Hidden
try {
if ($Seconds -gt 0) {
Start-Sleep -Seconds $Seconds
} else {
[void] [Console]::ReadLine()
}
} finally {
if (-not $process.HasExited) {
$process.Kill()
$process.WaitForExit()
}
}
}
$remoteLogDir = "/sdcard/Android/data/$resolvedPackage/files/logs"
$deviceLogsDir = Join-Path $sessionDir "device_logs"
New-Item -ItemType Directory -Force -Path $deviceLogsDir | Out-Null
Invoke-Adb shell ls -la $remoteLogDir 2>&1 | Tee-Object -FilePath (Join-Path $sessionDir "device_logs_ls.txt") | Out-Null
Invoke-Adb pull $remoteLogDir $deviceLogsDir 2>&1 | Tee-Object -FilePath (Join-Path $sessionDir "adb_pull_logs.txt") | Out-Null
$markerPattern = "connection\(0x[0-9a-fA-F]+, account[0-9]+, dc[0-9]+, type [0-9]+\)|connecting via proxy|mtproxy_startup|mtproxy_transport|mtproxy_disconnect|transport_state|endpoint_handshake_ok|endpoint_data_path_success|proxy_connection_stage|proxy_check_|proxy_check_scheduler|proxy_rotation|client_hello|client_hello_fragment|server_hello|first_tls|tls_alert|recv_eof|admission_|socket_connected|on_connected|TLS response|TLS server hello|TLS pending|ClientHello pending|socket error|EPOLLHUP|EPOLLRDHUP"
$markerPath = Join-Path $sessionDir "mtproxy_markers.txt"
$textFiles = @(Join-Path $sessionDir "logcat.txt")
$matches = foreach ($file in $textFiles) {
if (-not (Test-Path $file)) {
continue
}
Select-String -Path $file -Pattern $markerPattern | ForEach-Object {
"{0}:{1}: {2}" -f $_.Path, $_.LineNumber, $_.Line
}
}
if ($matches) {
$matches | Set-Content -Encoding UTF8 $markerPath
} else {
"No MTProxy markers found." | Set-Content -Encoding UTF8 $markerPath
}
$analysisPath = Join-Path $sessionDir "mtproxy_analysis.txt"
$analyzerPath = Join-Path $PSScriptRoot "analyze_mtproxy_markers.py"
function Convert-ToWslPath {
param([Parameter(Mandatory=$true)][string]$Path)
$fullPath = [System.IO.Path]::GetFullPath($Path)
if ($fullPath -match '^([A-Za-z]):\\(.*)$') {
$drive = $matches[1].ToLowerInvariant()
$tail = $matches[2] -replace '\\', '/'
return "/mnt/$drive/$tail"
}
return ($fullPath -replace '\\', '/')
}
$python = Get-Command python3 -ErrorAction SilentlyContinue
if (-not $python) {
$python = Get-Command python -ErrorAction SilentlyContinue
}
if ($python -and (Test-Path $analyzerPath)) {
$pythonPath = $python.Source
$analysisLines = & $pythonPath $analyzerPath $markerPath --out-dir $sessionDir 2>&1
$analysisLines | Set-Content -Encoding UTF8 $analysisPath
$analysisLines | Out-Host
} else {
$wsl = Get-Command wsl.exe -ErrorAction SilentlyContinue
if ($wsl -and (Test-Path $analyzerPath)) {
$analyzerUnix = Convert-ToWslPath $analyzerPath
$markerUnix = Convert-ToWslPath $markerPath
$sessionUnix = Convert-ToWslPath $sessionDir
$stdoutPath = Join-Path $sessionDir "mtproxy_analysis_wsl_stdout.tmp"
$stderrPath = Join-Path $sessionDir "mtproxy_analysis_wsl_stderr.tmp"
$process = Start-Process -FilePath $wsl.Source -ArgumentList @("python3", $analyzerUnix, $markerUnix, "--out-dir", $sessionUnix) -Wait -PassThru -NoNewWindow -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath
$analysisLines = @()
if (Test-Path $stdoutPath) {
$analysisLines += Get-Content $stdoutPath
}
if (Test-Path $stderrPath) {
$analysisLines += Get-Content $stderrPath
}
if (-not $analysisLines) {
$analysisLines = @("WSL analyzer produced no output.")
}
$analysisLines | Set-Content -Encoding UTF8 $analysisPath
$analysisLines | Out-Host
if ($process.ExitCode -ne 0) {
"WSL analyzer exited with code $($process.ExitCode)." | Add-Content -Encoding UTF8 $analysisPath
}
Remove-Item -Force -ErrorAction SilentlyContinue $stdoutPath, $stderrPath
} else {
"Python was not found; skipped MTProxy marker analysis." | Set-Content -Encoding UTF8 $analysisPath
}
}
$runtimeContractPath = Join-Path $sessionDir "mtproxy_runtime_contract.txt"
$verifierPath = Join-Path $PSScriptRoot "verify_mtproxy_runtime_logs.py"
if ($python -and (Test-Path $verifierPath)) {
Write-Host ""
Write-Host "Running MTProxy runtime contract verifier..."
$pythonPath = $python.Source
$runtimeContractLines = & $pythonPath $verifierPath $sessionDir 2>&1
$runtimeContractExit = $LASTEXITCODE
if (-not $runtimeContractLines) {
$runtimeContractLines = @("MTProxy runtime contract verifier produced no output.")
}
$runtimeContractLines | Set-Content -Encoding UTF8 $runtimeContractPath
$runtimeContractLines | Out-Host
if ($runtimeContractExit -ne 0) {
"MTProxy runtime contract verifier exited with code $runtimeContractExit." | Add-Content -Encoding UTF8 $runtimeContractPath
Write-Warning "MTProxy runtime contract verifier exited with code $runtimeContractExit. See $runtimeContractPath"
}
} else {
$wslVerifier = Get-Command wsl.exe -ErrorAction SilentlyContinue
if ($wslVerifier -and (Test-Path $verifierPath)) {
Write-Host ""
Write-Host "Running MTProxy runtime contract verifier through WSL..."
$verifierUnix = Convert-ToWslPath $verifierPath
$sessionUnix = Convert-ToWslPath $sessionDir
$stdoutPath = Join-Path $sessionDir "mtproxy_runtime_contract_wsl_stdout.tmp"
$stderrPath = Join-Path $sessionDir "mtproxy_runtime_contract_wsl_stderr.tmp"
$process = Start-Process -FilePath $wslVerifier.Source -ArgumentList @("python3", $verifierUnix, $sessionUnix) -Wait -PassThru -NoNewWindow -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath
$runtimeContractLines = @()
if (Test-Path $stdoutPath) {
$runtimeContractLines += Get-Content $stdoutPath
}
if (Test-Path $stderrPath) {
$runtimeContractLines += Get-Content $stderrPath
}
if (-not $runtimeContractLines) {
$runtimeContractLines = @("MTProxy runtime contract verifier produced no output.")
}
$runtimeContractLines | Set-Content -Encoding UTF8 $runtimeContractPath
$runtimeContractLines | Out-Host
if ($process.ExitCode -ne 0) {
"MTProxy runtime contract verifier exited with code $($process.ExitCode)." | Add-Content -Encoding UTF8 $runtimeContractPath
Write-Warning "MTProxy runtime contract verifier exited with code $($process.ExitCode). See $runtimeContractPath"
}
Remove-Item -Force -ErrorAction SilentlyContinue $stdoutPath, $stderrPath
} else {
"Python was not found; skipped MTProxy runtime contract verifier." | Set-Content -Encoding UTF8 $runtimeContractPath
}
}
"finished=$((Get-Date).ToString('o'))" | Add-Content -Encoding UTF8 $metaPath
Write-Host ""
Write-Host "Done."
Write-Host "Session directory: $sessionDir"
Write-Host "Markers: $markerPath"
Write-Host "Analysis: $analysisPath"
Write-Host "Runtime contract: $runtimeContractPath"