Ash Vlogs 频道首页二维码背后的"秘密"深入分析

in 恶意软件 with 0 comment

前言

近期在 YouTube 频道《脑洞乌托邦》观看了 2 个视频,分别是:

在上文中所列出的 2 支视频讲述的内容与本文主题相关联,也是促使我编写这篇文章的原因所在。如果您不了解 Ash Vlogs 事件始末,建议您先花一些时间观看上文所列出的视频内容。

分析

本文假定您已了解 Ash Vlogs 事件始末。在下文中,我将对 Ash Vlogs 事件频道《i_know_where_she_is》首页和最后一支视频中所出现的二维码进行深入分析。

二维码

通过访问 Ash Vlogs 事件频道《i_know_where_she_is》的首页,我们可以清晰看到在首页横幅和最后一支被上传的视频的预览缩略图中都存在一张二维码:

二维码

解码

为便于进一步深入分析,我们需要采用相对于直接使用移动设备扫码方式更加安全且易于跟踪的方式对这张二维码进行解码,这里我通过 Python 交互式 Shell 使用 Zbar 库实现:

>>> from PIL import Image
>>> from pyzbar import pyzbar
>>> pyzbar.decode(Image.open('./1.png'))

其中,文件 1.png 是位于当前 Python 交互式 Shell 工作目录下与上文图中所示一致的二维码图像。上文 Python 交互式 Shell 运行结果:

[Decoded(data=b'https://tinyurl.com/y6yzvt3p', type='QRCODE', rect=Rect(left=6, top=6, width=189, height=190), polygon=[Point(x=6, y=6), Point(x=6, y=196), Point(x=195, y=196), Point(x=195, y=7)])]

当然,另一种选择是使用互联网上提供在线二维码解码工具的网站。

解码后的链接

在上文中我们将二维码解码后得到一串被编码在二维码中的 URL 链接,根据经验这似乎是一个短链接。当我们访问短链接时,短链接服务提供商会将我们重定向到真正的 URL 链接。接着尝试从短链接中剥离出域名 tinyurl.com 并访问它,以验证我的猜测:

短链接服务提供商

从上图中不难看出这的确是一家短链接服务提供商,被编码到二维码中的 URL 链接也的确时一个短链接。

获得真正的链接

短链接在表面上会隐藏真正的 URL 链接,在下文中我将通过 Microsoft Windows PowerShell 尝试请求它以得到短链接服务提供商所重定向的真正 URL 链接:

(Invoke-WebRequest -Method HEAD "https://tinyurl.com/y6yzvt3p" -MaximumRedirection 0 -ErrorAction Ignore).Headers.Location

以上 PowerShell 命令运行后输出:

https://drive.google.com/drive/folders/15dyKeivBi1vRdybkcRYgkokxlyNy6GCC?usp=sharing

当然,另一种选择是使用 Web 浏览器直接访问短链接,通常情况下浏览器将自动跳转到真正的 URL 链接。

可执行文件

在上文中,我们通过解码二维码获得一个短链接,又通过 PowerShell 请求短链接获得真正的 URL 链接。从 URL 链接中可以得知这是一个 Google Drive 分享链接:

在 Google Drive 分享的文件

从上图中我们可以看到,一位名为 i_know_where_she_is 的 Google 用户在 Google Drive 分享了一个名为 c2f 的文件夹,其中包含一个名为 click.zip 的压缩包文件。

接着,我们从 Google Drive 上下载名为 click.zip 的压缩包文件,然后将其解压缩:

click.zip 压缩包内文件

click.zip 压缩包内文件详情

基本信息

解压缩后得到 2 个文件,其静态扫描基本信息如下:

文件名类型数字签名SHA1
click.exePE32+ executable (console) x86-64, for MS Windows没有3B32300D21B015A0555133362CC6BA921A9C4BF4
drip.icoMS Windows icon resource - 1 icon, 150x150, 32 bits/pixel没有B5C4A0E361185D8010CD11809C1FAF6F45766AB2

Tips: 静态扫描有助于确定目的文件的真实类型是否与其扩展名(后缀)相关,但通常情况下可能无法正确识别有意的文件类型伪装(例如包括但不限于:伪装文件头和结构、文件尾部追加数据等...)。

安全性

值得注意的是:经过分析,我在上文所列出的 2 个文件中没有找到任何恶意代码,均为干净文件。因此您可以放心地在您的 PC 设备上下载并运行它,不会给您的设备带来威胁。

您需要确信所下载并运行的文件与上文所列出的文件 SHA1 校验结果一致,否则安全性是未知的。

动态分析

通过对压缩包内文件的静态扫描分析得知,文件 click.exe 是一个 64 位二进制可执行文件(PE)。那该可执行文件运行生命周期内究竟进行了那些工作呢?别着急...现在我们将可执行文件 click.exe 放置在动态分析环境中运行,以分析其工作行为:

click.exe 运行时截屏

上图所示为可执行文件 click.exe 运行后截屏,从图中我们可以看到可执行文件 click.exe 依赖于命令提示符主机进程,因此推测该可执行文件zi子系统为 WINDOWS_CUI

click.exe 运行时关键行为

...

click.exe 运行时关键行为

从上图中我们可以看到,可执行文件 click.exe 实现了释放器功能,运行后在当前用户临时目录下释放了许多依赖文件。从其释放的文件中可以发现包括但不限于 CPython 解释器所提供的 Python 标准库二进制可执行文件(*.pyd)、CPython 3.6 解释器核心二进制可执行文件(python36.dll)和 Visual C 运行时库二进制可执行文件等。

通过上述行为,我们可以推测出可执行文件 click.exe 本质是一个 Python 源文件包装器(外壳),其将一个或多个 Python 源文件编译并包装为一个二进制可执行文件。

click.exe 运行时关键行为

从上图中我们可以看到,可执行文件 click.exe 完成依赖文件释放工作后为自身创建了一个子进程,从子进程的行为不难看出其正在充当真正 Python 代码的加载引导和解释器角色。

click.exe 父子进程树

click.exe 父子进程关系

除此之外,我们可以看到可执行文件 click.exe 试图与 Google Public DNS(IPv4: 8.8.8.8)的 53 端口建立 TCP/IP 连接,猜测其正在试图检测当前运行环境是否具有良好的互联网连接。

click.exe 与 Google Public DNS 建连的数据包

由于外联网安全性等因素,这边企业内部的 Workspace 流量防火墙不允许工作网络内联网设备直接外联到公网 DNS 服务器进行 DNS 查询操作,因此与 Google Public DNS 建立 TCP/IP 连接的数据包会被丢弃,最终表现为连接超时。相信这也是可执行文件 click.exe 消息框提示“connect to internet in 10 Seconds or the game ends”的原因所在。

click.exe 退出时行为

当我们在动态分析环境中交互式点按“OK”按钮时,可执行文件 click.exe 会再次通过相同的方式检查互联网连接的有效性。如果依旧无法与 Google Public DNS 成功建立 TCP/IP 连接,将清理先前释放的临时文件并通过创建新的命令提示符进程并执行命令以删除自身:

click.exe 删除自身

从上图中命令提示符进程的命令行我们可以看到,其尝试执行 Ping 操作到本机 3 次并通过重定向 Stdout 流的形式将结果写入当前工作目录的 num 文件中,然后删除自身二进制可执行文件。Ping 每次操作之间会默认间隔 1 秒,我猜测这是为了使可执行文件 click.exe 有时间清理释放的临时文件,以免自我删除因进程实例未退出(本质上是该二进制可执行文件被其进程实例映射)而失败。

静态分析

在上文的动态分析中我们得知,可执行文件 click.exe 本质上是一个 Python 源文件包装器(外壳),其功能使用 Python 语言编写并依赖于 CPython 3.6 解释器实现。目前知名的将 Python 源文件编译为字节码并包装成 Windows 可执行文件的开源解决方案有:

在下文中,我们将通过对二进制可执行文件 click.exe 进行静态分析,以试图确定它是否使用了以上任意一种开源解决方案。同时结合静态分析,也将帮助我们更全面地了解该可执行文件 click.exe 的功能实现。

Tips: 为便于分析时理解,我会将已分析确定实现功能的函数重命名为便于人类阅读的名称,也会为相关代码片段编写注释。这些重命名后的函数符号并非来自于可执行文件 click.exe 的调试符号表,不能替代其函数的真实符号名,仅供参考。

click.exe 用户代码入口函数

上图所示为可执行文件 click.exe 入口函数,该函数在 C 运行库完成初始化后被调用。入口函数的逻辑较为简单,其将当前自身进程实例的命令行参数由宽字符转换为 UTF-8 编码的多字节数据后跳转到另一个负责进行初始化的函数,我们暂且将其称之为 __bootloader_main

从上文中我们得知,可执行文件 click.exe 的功能是使用 Python 编写的,因此此处转换命令行参数编码的用意猜测是为了将其传递给 CPython 3.6 解释器,以便于真正实现功能的 Python 代码正常获取命令行。

Bootloader 流程

Bootloader 流程

Bootloader 流程

如上图所示,可执行文件 click.exe__bootloader_main 函数主要尝试分配一块用于存储 Archive 状态信息(元数据)的内存;然后尝试获取当前进程实例中名为 _MEIPASS2 的环境变量值,无论上一步是否成功取得有效值均尝试从当前进程实例删除一个名为 _MEIPASS2 的环境变量;最后尝试调用另一个函数从当前可执行文件获取与 Archive 相关的元数据,暂且将其称之为 __initialize_archive。上述逻辑任何一步失败均会导致程序退出整个流程。

那 Archive 究竟是什么呢?大家可能已经猜到,Archive 中存储了真正实现功能的 Python 字节码、CPython 3.6 解释器和依赖库。

click.exe 区段信息

我们通过解析可执行文件 click.exe 的区段信息可以看到其尾部拥有额外的扩展数据:

click.exe 区段转储

将尾部的扩展数据段转储后可以发现文件大小高达 48.7 MB,这便是 Archive。

Initialize Archive 相关流程

Open Archive 相关流程

Open Archive 流程

Open Archive 流程

Open Archive 流程

如上图所示,函数 __initialize_archive 主要实现了从自身可执行文件的扩展数据段中解析读取 Archive 元数据到内存的功能,其元数据包括但不限于 Archive 实际长度、文件列表(TOC)、文件列表长度(TOCLEN)、Python 解释器版本和 Python 解释器动态链接库名称等。接着,我们回到函数 __bootloader_main 调用 __initialize_archive 后的逻辑:

添加临时目录路径到 DLL 装载文件搜索路径

所以环境变量 MEIPASS2 为什么存储 Archive 临时文件释放目录呢?别着急...我们接着往下看:

Bootloader 相关流程

Bootloader 相关流程

还记得在上文中提及可执行文件 click.exe 会创建自身的子进程吗?如上图所示,如果之前成功获取到环境变量 MEIPASS2 的值,则认为当前进程是子进程,进而调用一个函数以进行加载并初始化 CPython 3.6 解释器等相关工作;否则,当前进程是父进程,进而调用另一个函数创建自身的子进程。

这里大家可能会有疑惑,环境变量 MEIPASS2 是在父进程设置的,仅局限于父进程实例本身,为何子进程有可能成功获取呢?这是因为子进程实例在创建时会继承父进程的环境变量和值。

Bootstrap 相关流程

上图所示为函数 __bootstrap 所实现的逻辑,其主要功能是加载 CPython 3.6 解释器的动态链接库 python36.dll 到当前进程空间;然后创建一个 Python VM 实例并初始化一些参数;接着从之前加载的 Archive 元数据中找出 TOC 并遍历每条记录,将记录对应的数据段从可执行文件的附加数据段中读入,并使用 zlib 库解压后通过 Python 标准库 marshal 将其反序列化为 Python 模块对象后在 Python VM 中执行,大致流程如下:

Bootstrap 相关流程

Bootstrap 相关流程

以上便是函数 __bootstrap 的运行逻辑,让我们再来看看函数 __create_child_process

创建子进程相关流程

创建子进程相关流程

综上所述,我们可以得知可执行文件 click.exe 的启退流程如下:

click.exe 启动流程示意图

当然,以上启退流程示意图中不包括 click.exe 所执行的 Python 代码的启动和退出逻辑。所以可执行文件 click.exe 是否使用了开源解决方案将 Python 源代码编译并包装为 Windows 可执行文件(PE)呢?使用了哪一种呢?答案在 Bootloader 流程。

从上文已有的静态分析结果中我们可以看到在 Bootloader 流程中,函数 __bootloader_main 在内部调用函数 __initialize_archive 实现对 Archive 元数据的解析操作,而背后真正实现解析功能的是函数 __open_archive

值得注意的是,在函数 __open_archive 内部通过调用函数 __get_cookie_for_archive 实现在当前可执行文件的附加数据段中定位 Archive 元数据偏移量并将其读入到事先已分配的缓冲区。

函数 __get_cookie_for_archive 在内部通过一段固定的特征码(Magic)来定位 Archive 元数据:

Archive Magic

Archive Magic

Archive Magic: 4D 45 49 0C 0B 0A 0B 0E

Archive Magic

该特征码(Magic)属于知名开源解决方案 pyinstaller,这意味着可执行文件 click.exe 是使用 pyinstaller 将 Python 源代码编译为字节码并包装为 Windows 可执行文件(PE)的。

而接下来我们需要做的是,根据上文中静态分析所得出的 pyinstaller 包装方式和数据结构从可执行文件 click.exe 中提取真正实现其功能逻辑的 Python 源文件的字节码文件(*.pyc)并进一步静态分析:

Python 导入依赖

Python 导入依赖

如上图所示,可执行文件 click.exe 的 Python 功能逻辑(在下文简称为“主模块”)开始时向当前运行环境导入了一些依赖的标准库模块和外部模块,这些模块分别是:

名称来源备注
socket标准库通常情况下用于网络通信
time标准库...
sys标准库...
threading标准库通常情况下用于创建和管理超线程
tkinter标准库Python GUI 软件包
subprocess标准库通常情况下用于创建和管理子进程
readchar外部依赖通常情况下用于读取单个字符
cv2外部依赖OpenCV 软件包,通常用于计算机视觉处理

从上文中可以看到其从标准库模块 tkinter 中导入了子模块 messagebox,相信大家从模块名中不难看出该模块主要的功能是实现绘制一个可交互的消息对话框。

在向当前运行环境导入所依赖的模块后,主模块创建(定义)了多个函数和变量。为了不打乱静态分析顺序,我们直接跳转到主模块所执行的功能代码处:

创建主窗口

首先主模块实例化类 tk.Tk,并将其实例赋值给变量 root,现在变量 root 代表主模块所创建的主对话框窗口;接着调用其实例方法 withdraw 将主窗口绘制推迟;然后其实例方法 iconbitmap 将可执行文件 click.exe 进程实例工作目录下的图标文件 drip.ico 设置为主对话框窗口的图标。

检查互联网连接

检查互联网连接

如上图所示,主模块试图调用其函数 internetCheck 来检查当前运行环境是否可以正确连接到互联网,如果成功则继续执行,否则弹出消息框恐吓用户连接到互联网。在恐吓用户的消息框被关闭后主模块将自行挂起 10 秒钟,之后再次调用函数 internetCheck 检查互联网连接,如果成功则继续运行,否则退出自身。

还记得我们在动态分析时所遇到的消息框“connect to internet in 10 Seconds or the game ends”吗?没错,就是位于上文所提及的逻辑。让我们进入主模块的函数 internetCheck 一探究竟:

internetCheck 函数

internetCheck 函数

从主模块函数 internetCheck 的运行逻辑和主模块对其的调用逻辑可以看出,其主要尝试与远程主机 8.8.8.8 的端口 53 (也即 Google Public DNS)建立 TCP/IP 连接以检查互联网连接是否可用。

接着我们回到主模块调用其函数 internetCheck 之后,从上图我们可以看到其又分别调用了主模块函数 storagePermcameraPermmicPermlocPerm。这几个函数的运行逻辑较为简单,主要就是弹出一系列访问当前存储介质、摄像头、麦克风和物理位置等信息的恐吓消息框。如果用户同意则弹出消息框 good choice,否则调用主模块函数 exitt 退出自身进程(该函数在下文会进行分析)。当然,值得注意的是主模块的确会访问使用当前设备已连接的摄像头设备。让我们继续往下看:

循环接受用户输入

循环接受用户输入

让我们一起看一下主模块函数 wrongAttempt

wrongAttempt 函数

如上图所示,主模块会输出一些问题并从控制台(Stream: Stdin)接受用户输入,然后将每一个问题的用户输入内容赋值给一个变量。之后在循环中检查用户输入是否与预设的一个或多个输入相匹配,如果匹配则跳出循环继续运行,否则调用主模块函数 wrongAttempt 后重复这一过程。

主模块函数 wrongAttempt 主要实现了将全局变量 Attempts 的值减 1,然后弹出消息框警告用户还剩余几次机会。如果全局变量 Attempts 的值为 0,则调用主模块函数 exitt 退出自身进程。

循环接受用户输入

循环接受用户输入

接着要求用户输入一个密钥,如果密钥与预设值匹配则继续运行,否则退出自身进程:

循环接受用户输入

如果密钥与预设值匹配,则循环连续打印 400 次 good drip.

循环接受用户输入

循环接受用户输入

循环接受用户输入

循环接受用户输入

循环接受用户输入

然后便是重复询问前 3 个问题。与之前不同的是,第三个问题将需要正确回答 4 次:

循环接受用户输入

循环接受用户输入

循环接受用户输入

值得注意的是,主模块此处并未验证用户输入的答案内容是否与问题预期答案一致,也即输入的字符个数相等即可通过验证并继续运行。

循环接受用户输入

然后主模块会打印输出一个 true。由于篇幅长度问题且接下来提问逻辑几乎一致,我将直接列出与问题对应的预设答案供大家参考:

问题预设答案
k2y%9hL_*q@eAWS5sd
would you like to knowyesy
would you like to seeyesy
k3xG%rDL4_$2$WHhex
is it okay to want to dieyesy
is it okay to see deathyesy
is it okay to killyesy
k44dCp!u=A7Mw3v?KN

在问题 is it okay to kill 正确匹配预设答案后,主模块将尝试开启本机已连接的摄像头并通过预览对话框显示从摄像头捕获的图像:

显示摄像头预览

如上图所示,主模块在当前进程实例创建了一个超线程并将线程起始函数设置为主模块函数 show_webcam,然后打印输出 smile.。让我们看看函数 show_webcam 做了哪些工作:

显示摄像头预览

显示摄像头预览

可见主模块函数 show_webcam 主要实现了通过 OpenCV 软件包打开当前设备的摄像头并每间隔 1 毫秒捕获一次画面,然后将捕获的画面显示在预览对话框内。

嗯...小乌视频中当时位于预览对话框内的表情非常适合表达许多不明真相的吃瓜群众当时的心情。

法律信息:图像来自 《直播视频中隐藏的线索 背后的组织究竟是谁??Ash Vlogs事件始末(下)》 视频,版权和肖像权所有者的联系方式是 《脑洞乌托邦》,若有侵权请转到 投诉与反馈 页面。

言归正传,紧接着主模块将要求用户输入第四阶段的密钥,预设密钥值我已在上文中列出。如果密钥与预设值相匹配,主模块将按顺序打印如下内容:

最后弹出一个消息对话框,其内容为 email true_drip@protonmail.com. subject line: dirty hands. your mailing address in the body. we will only send it to the first.,然后调用主模块函数 exitt 退出自身进程。下面我们一起来看一下在上文多次出现的 exitt 函数做了些什么:

exitt 函数

还记得我们在动态分析时所提到的可执行文件 click.exe 在退出时会创建一个命令提示符子进程来删除自身位于硬盘上的可执行文件的行为吗?没错,该行为在主模块函数 exitt 中实现。

最后

以上便是对 Ash Vlogs 事件频道《i_know_where_she_is》首页和最后一支视频中所出现的二维码的全部分析内容。如果您已观看过文章开头所提及的 2 支视频,相信您已了解 Ash Vlogs 所发生的事情并不是真的。

威胁定义

值得强调的是,在本文中所涉及的可执行文件 click.exe 非常干净,其中没有任何恶意代码,因此您可以放心地在自己的设备上运行它。严格意义上来说,该可执行文件应该被定义为玩笑程序。玩笑程序不是恶意软件,不会对设备产生任何实际威胁和破坏性。玩笑程序通常试图伪装成恶意软件,因此可能会对使用者造成混乱而产生有害影响,防病毒软件默认情况下不会检出并拦截它。

请注意,您仍需检查您所运行的可执行文件是否与本文所列出的可执行文件的 SHA1 校验值一致,否则其安全性是未知的。如果您不确定您运行的可执行文件 click.exe 是否安全,可以发送邮件至 king@xiaoyy.org,我将在工作空闲时间手动快速筛查这些文件的安全性。

纠错

这篇分析文章是在工作和生活闲余时间编写的,并非一气呵成,因此文中可能存在一些错误或描述不清晰的地方。如果您认为本文内容存在错误点,不要犹豫,直接发送电子邮件到 king@xiaoyy.org 并附上您的观点,我将定期检查这些邮件并核实、纠正。

分享您的想法 (2018/3/7 之前的评论与回复将不再显示)