一、概述

McAfee 近期研究发现,部分安卓恶意软件正滥用 .NET MAUI 跨平台框架(原文链接)来规避检测。这些恶意样本伪装成合法应用,试图窃取用户敏感数据。天穹沙箱具备强大的安卓样本分析功能,下面将结合报告中的样本行为进行深度解析,揭示其攻击手法。

二、.NET MAUI

.NET MAUI(.NET Multi-platform App UI)是一个基于 .NET 6+ 的跨平台框架,于2022年5月发布,它由 Xamarin 演变而来,MAUI 继承了 Xamarin 的底层架构和机制,旨在利用 C# 和 XAML(Extensible Application Markup Language)进行高效的共享 UI 设计来简化跨平台应用程序开发。

图1 .NET MAUI平台架构

在 Android 上,MAUI 提供了 .NET 对 Android.*Java.* 命名空间的绑定。MAUI.Android 应用程序在 Mono 托管执行环境中运行,同时 Android Runtime(ART)虚拟机也在并行运行。Mono 通过 MCW(Managed Callable Wrappers)桥接调用 Java 代码,包括对 Java 类型的子类化和虚方法的重写。当 ART 需要调用托管代码时,则通过另一种 JNI 桥接机制——ACW(Android Callable Wrappers)实现。两个运行时环境共同运行在 Linux 内核之上,并调用各自的 API 来执行用户代码。

大致运行原理如下:

  1. .NET 代码被编译为 IL(中间语言);

  2. 部署到 Android 设备后,借助 Mono 运行时(Runtime),IL 会在应用运行时被即时编译(JIT)为 Android 平台上的本地指令(ARM 或 x86);

  3. UI 层由 .NET MAUI 控件驱动,但最终仍会调用 Android 原生 API(例如View、Activity、Intent等)。

图2 MAUI.Android运行机制

三、样本信息

  • 样本名称:16a4850100d3633bde5d462c6d896416.apk

  • SHA1:a5d8089d319962a6a4aede46f1c89bc52480978e

  • 文件类型:APK

  • 样本大小:29.47MB

  • 样本分析报告:天穹沙箱分析报告

四、样本分析

静态检测

报告样本详细信息部分展示了沙箱静态分析引擎提取到的样本图标和元数据信息,该样本将自身图标伪装为 X(Twitter),如图3所示,误导用户认为该 APK 是合法应用。

图3 图标信息

从 APP 中提取到的元数据信息如图4所示,关键元数据如下:

  • Main Activity 名为 crc6496075835fbf2e46c.MainActivity
  • 应用包名 ppI74T.cgDdFK 包含明显的混淆;
  • APK 名称为X
  • SDK 兼容 Android 5.0 到 Android 12 的设备。

图4 APP元数据信息

另外,该样本申请了以下权限:

  • android.permission.READ_CONTACTS:读取联系人信息(包括姓名、号码、邮箱等);
  • android.permission.READ_SMS:读取短信内容(包括验证码);
  • android.permission.READ_EXTERNAL_STORAGE:读取外部存储文件(图片、文档等),从 Android 10 开始受限制;
  • android.permission.INTERNET:访问网络;
  • android.permission.ACCESS_NETWORK_STATE:获取网络状态(是否联网、是否 Wi-Fi);
  • com.uPcA.WaDVOZnIP.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION:定义了一个内部广播权限,保护级别为 signature(同签名才能使用)。

除此之外,还存在大量混淆的自定义权限名,如图5所示,这些权限并非是标准权限,主要用来干扰静态分析工具。

图5 权限信息

样本组件信息如图6所示,Receivers 组件定义了一个名为 crc640a8d9a12ddbf2cf2.EnergySaverBroadcastReceiver 的节能类接收器,但其命名明显经过混淆处理;Providers 组件中则使用了 Mono 框架的 mono.MonoRuntimeProvider,Mono 是一个用于在 Android 等平台上运行 .NET 应用的运行时环境,通常用于 Xamarin.Net MAUI 应用的托管执行环境。因此,该组件信息表明样本很可能基于 .NET 开发,并可能携带 .NET 程序集相关资源。

图6 组件信息

进一步分析样本的资源目录,发现存在一个名为 assemblies 的目录,如图7所示,内容结构与典型的 Xamarin/.NET MAUI 应用相符。

  • manifest :包含文件名、ID、相关的哈希值以及偏移量的索引;

  • assemblies.blob:包含一个或多个 .NET 程序集(.dll 文件);

  • assemblies.[架构].blob:针对特定架构生成的程序集的压缩文件;

  • rc.bin:与 .NET 各种功能和特性相关的配置文件,其中启用了以下几项:

    1. System.AggressiveAttributeTrimming:启用激进的属性修剪。在发布时,未使用的属性可能会被移除,以减小应用程序的大小。
    2. System.Net.Http.UseNativeHttpHandler:启用使用原生 HTTP 处理程序,使用平台特定的 HTTP 实现。
    3. System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault:JSON 序列化器默认启用反射。
    4. Switch.System.Reflection.ForceInterpretedInvoke:强制解释调用。在使用反射时,所有调用都将通过解释执行,而不是通过编译执行。
    5. Microsoft.Extensions.DependencyInjection.DisableDynamicEngine:禁用动态引擎。在依赖注入过程中,使用静态解析,而不是动态解析。

图7 assemblies目录

此外,在 lib 目录发现一个名为 libaot-Killer-2-Maui.dll.so 的文件,如图8所示,该文件是由 .NET 程序集 Killer-2.Maui.dll 经过 AOT(Ahead-Of-Time)编译后生成的本地共享库文件。

图8 libaot-Killer-2-Maui.dll.so

动态分析

该样本运行后是 X(Twitter) 应用登录页面。提示信息为中文,要求输入手机号码和 ID,如图9所示。

图9 登录页面

使用沙箱远程桌面进行交互操作,输入相关信息后,显示匹配中,请稍等,如图10所示。

图10 等待界面

沙箱动态分析过程会自动授予应用所需的权限,如果在远程桌面中手动关闭权限,应用会弹出权限申请说明,引导用户授予权限,如图11所示。

图11 权限申请说明

查看报告的动态行为类目,样本运行后从自身资源文件中读取了 YsFxIQL32GX6W9GAqiNNeUJmAF8o88LK/u1cXgTeF,后续存在写入行为,写入文件为 YsFxIQL32GX6W9GAu1cXgTeF,如图12所示。

图12 读取资源文件并写入

使用了 AES-CBC 算法解密文件,采用 PKCS5 填充方式,密钥十六进制字符串为76baa89ee594d8e6c3eb7172b27c9b45,动态加载并执行了可疑代码 YsFxIQL32GX6W9GA。另外创建了两个名为 /data/data/<package>/files/.native_storage/data/data/<package>/files/oat 的目录,如图13所示。

图13 AES解密并动态执行可疑代码

沙箱支持衍生物提取,查看分析衍生物,已经成功提取到样本写入的两个文件 YsFxIQL32GX6W9GAu1cXgTeF,如图14所示。点击中间文件下载,即可下载衍生物。

图14 分析衍生物

报告联网活动追踪部分记录了样本成功触发 C2,C2 地址为120.27.233[.]135,端口为1833,目前已失活。该地址被威胁情报判定为 Android 窃密木马活动,如图15、16所示。

图15 联网活动追踪

图16 威胁情报

壳入口点分析

cgDdFK 是一个高度混淆的 Application 类,其通过重写 ContextWrapper 中的 attachBaseContext 方法,在应用启动早期阶段执行关键操作:从 assets 目录读取加密文件,使用 XOR 解密后写入私有目录,并动态加载 com.example.virusscanbypassbootstrapper.DexLoader 类,随后通过反射调用 loadDex 和 hook 方法,实现外部 Dex 文件的加载与执行。在 Android 应用中,Application 类的 attachBaseContextonCreate 方法是应用执行最早的入口,分别负责与上下文的绑定以及全局初始化逻辑。加壳技术正是利用这两个方法作为主要切入点,通过动态加载与反射机制,实现代码隐藏与运行时注入,从而达到绕过静态分析与病毒扫描的目的。

图17 DexLoader 动态注入

zeiv2hohshoe2Aiy 方法用于从 assets 目录中读取了一个文件,之后使用 XOR(异或)解密,接着把解密后的内容写入应用私有目录,并改变写入文件的可写属性(从可写改为不可写)。

图18 zeiv2hohshoe2Aiy方法

cgDdFK类中包含一些未被完全混淆的的字符串,如:loadDexcom.example.virusscanbypassbootstrapper.DexLoaderhook,这些与加载 Dex 文件、绕过病毒扫描、钩子技术相关。

图19 部分字符串

衍生物分析

衍生物一:YsFxIQL32GX6W9GA

YsFxIQL32GX6W9GA 是由应用的入口类 cgDdFK 在运行时通过异或解密生成的dex文件。在该文件中使用了两个包命名空间:com.example.initialloadercom.example.virusscanbypassbootstrapper

在 com.example.initialloader 中可以更为清晰地看到 attachBaseContext 方法的实现,如图20所示,具体为:

1.从 assets 目录中读取加密的 bs2.dex 文件,使用硬编码的密钥字符串进行按字节 XOR 解密,将解密后的内容保存为新的 .dex 文件到私有目录,密钥为 Sepwo2js3jGLLFiJ97RKdBDFpNc30ANAneBDPOAm04fvWltJ71vhfBYc9qB03A7Z

2.通过 PathClassLoader 加载该dex中的 com.example.virusscanbypassbootstrapper.DexLoader 类;

3.反射调用静态方法 DexLoader.loadDex,加载 classes.zip 文件。

图20 com.example.initialloader

类DexLoader分析

DexLoader 类通过解密资产中的 DEX 和本地库,实现应用启动时的动态加载,从而绕过静态分析与检测。

loadDex方法

loadDex 方法是一个动态Dex加载器,从 assets 目录中加载 classes.zip 文件,调用 openAssets 方法解密 dex 文件,根据不同的 Android 版本以不同方式动态加载这些 dex 进 App 的 ClassLoader,最终完成动态代码注入。同时,它还将该 zip 的路径添加到 native 库查找路径中,用于加载.so文件。

根据不同的 Android 版本选择不同的加载方式:

  • API >= 29(Android 9+):使用 makeInMemoryDexElements 方法从内存中直接加载 dex 文件,无需写入磁盘;
  • API >= 26(Android 8+):通过自定义 DexClassLoader 实例化加载,即使用 InMemoryDexClassLoader 从内存中直接加载 dex 文件;
  • API < 26(Android <8):写入 dex 文件到应用私有目录中的 .dex_load 文件夹,再通过 DexClassLoader 加载。

图21 loadDex方法

openAssets方法

基于文件名生成 AES 密钥,使用 CBC 模式解密 asset 文件并返回解密后的内容。这与动态行为中解密生成 u1cXgTeF 文件一致。

图22 openAssets方法

addNativeLibrarySearchPath方法

通过反射修改 ClassLoader 的本地库加载路径,将 .native_storage 目录添加为搜索路径,实现兼容不同 Android 版本的 .so 文件动态加载。

图23 addNativeLibrarySearchPath方法

hook方法

动态替换 Application 实例,通过反射的方式钩住一个 Application 实例,并调用它的 attach 方法。Attach 方法是 Application 类的一部分,用于初始化应用。

图24 hook方法

replaceApplicationContext方法

将当前应用的 Application 替换为一个新的 Application 实例。

图25 replaceApplicationContext方法

dropDynamicNativeLibraries方法

提取符合条件的 .so 动态链接库文件,存储到设备的本地存储中,并返回存储的文件路径列表。

图26 dropDynamicNativeLibraries方法

衍生物二:u1cXgTeF

u1cXgTeF 是通过 loadDex 方法经 AES 解密后的 ZIP 文件,ZIP 中包含了 dex 文件。dex 文件中定义了应用的入口点,即 crc6496075835fbf2e46c.MainActivity。在 MainActivity 中注册了 n_onCreate 方法,用于与 Mono 运行时进行交互。随后,应用通过 TypeManager.Activate 方法,尝试在 Killer-2-Maui 程序集中查找并加载名为 Killer_2_Maui.MainActivity 的类。

图27 MainActivity类

Killer-2-Maui分析

在 Xamarin 和 Mono for Android(即 Mono.Android)中,C# 代码会被编译成 .NET 程序集(DLL),然后与其他 Android 应用程序资源一起打包。传统方式上,Xamarin.Android 会将这些程序集中 DLL 文件存储在 libmonodroid_bundle_app.so 文件中。这是一种特殊的共享库,它包含了 C# 程序集、Mono runtime 库、以及其他资源,打包在一起用于 Android 应用。

在 Android 12 及以后的版本中,Xamarin 和 Mono for Android 进行了改进,不再将所有 DLL 打包在 libmonodroid_bundle_app.so 文件中,而是改用 assemblies.blob 文件来存储这些程序集。这种方式分离了程序集和共享库文件,改善了资源打包问题,提高了程序的性能,同时减少了配置错误。MAUI 继承了 Xamarin 中的 assemblies.blob 打包方式和性能优化,进一步提升了跨平台开发体验。

经过多层解混淆和解密后,最终调用了 Killer-2-Maui 程序集中的类。样本 lib 库目录下的 libaot-Killer-2-Maui.dll.so 是 AOT 编译后生成的本地共享库文件,在编译过程中,IL 会被编译成对应平台(例如 ARM 或 x86)的机器码,.dll.so 文件里没有完整保存原来的 .NET 程序集结构,因此需要去 assemblies.blob 中解析和提取程序集文件。可以使用 .NET for Android 提供的工具 decompress-assembliesxamarout 等工具来解包 .NET 程序集(DLL)文件。

Killer-2-Maui.dll 包含的 PDB 路径信息为 /Users/tohis/Killer-3-Maui-builder/build-tmp/Killer-2-Maui/obj/Release/net8.0-android/Killer-2-Maui.pdb,从 PDB 信息中可以看到该样本使用了 .NET MAUI for .NET 8( .NET MAUI 8),表明该应用基于 .NET 8 和 MAUI 8 构建,目标平台为 Android(net8.0-android)。

图28 Killer-2-Maui.dll

Killer-2-Maui.dll 主导了应用的权限申请和输入校验行为,在获取到所需权限和校验通过后,样本将收集用户的敏感数据发送给 C2 服务器,具体如下:

  • 检查和申请权限,如果未被授予则会引导用户到设置页面进行手动授权
图29 检查权限授予情况
  • 检查输入的手机号长度是否是11位,以及手机号的前两位是否为13、15、17、18 或 19。
图30 检查输入的手机号
  • 收集短信(SMS)并将其以列表形式返回。
图31 收集短信
  • 收集设备中外部存储的图片,如相机拍摄的照片、截图等。
图32 收集外部存储的图片
  • 收集通讯录中的所有联系人。
图33 收集通讯录信息
  • 检查当前设备的网络连接是否可用,通过发送 HTTP 请求到阿里云 OSS 来验证网络的可用性。
图34 检测网络连接是否可用
  • 创建 SecClient 实例,指定远程主机(120.27.233[.]135)和端口(1833)。
图35 SecClient实例
  • 上传联系人信息(包括姓名和电话号码)。
图36 上传联系人信息
  • 上传短信记录,包括短信的内容、时间、类型、发送者和接收者信息。
图37 上传短信记录
  • 上传 Base64 编码的外部存储图片数据。
图38 上传图片数据
  • 通过 TCP 连接与 C2 进行消息的加密发送和解密接收。
图39 C2通信
  • C2 通信时使用 AES CBC 模式进行加密和解密,且通过密钥派生算法(MD5 + PBKDF2)生成 AES 的加密密钥。
图40 CryptoHelper

五、MAUI/Xamarin APK特征

通过上述分析以及解压后的 APK 目录文件信息列举,总结 MAUI/Xamarin 构建的 APK 特征如下:

1.APK 解压后的文件目录:

  • .NET MAUI 8:在 assemblies 目录下,包含 assemblies.manifest、assemblies.blob、assemblies.[架构].blob 等文件;
  • .NET MAUI 9:在 lib 目录下,包含 assemblies.[架构].blob.so.manifest、libassemblies.[架构].blob.so 等文件。在 .NET MAUI 9 中,Android 应用程序中 .NET 库的打包方式发生了重大变化,所有库都嵌入在名为 libassemblies.[架构].blob.so 的ELF (.so) 文件中,如 assemblies.arm64-v8a.blob.so。对于 .NET MAUI 9 可以使用 pymauistore 提取其中的程序集。

2.AndroidManifest.xml:应用清单中包含与 MAUI/Xamarin 相关的内容:

  • android:name 为 mono.MonoRuntimeProvider,含义为使用 Mono 运行时;

  • android:authorities为com.[包名].mono.MonoRuntimeProvider.__mono_init__,含义为初始化 Mono 运行时。

3.类名:包名通常形如 crc64[hash值].[类名],这种 crc64... 命名模式是由 .NET 构建系统自动生成的,MAUI 使用了 Mono 和 .NET IL 转 Java 的桥接机制,在构建 Android 应用时会生成一些自动注册的 Java 类或 Xamarin 类,这些类都是通过 MAUI/Xamarin.Android 的 IL 到 Java binding 自动生成的中间类,负责与 Android Framework 的集成。

4.lib 目录中可能包含以下相关的底层运行库:

共享库名称 说明 MAUI Xamarin
Mono.Android.dll.so 管理与 Android 系统的交互,处理应用程序的生命周期、资源管理等。
Mono.Android.Runtime.dll.so 启动 Mono 运行时环境并执行应用程序的 C# 代码。
libmonodroid.so 连接 Android Java 层与 Mono 托管层的核心桥接库。管理应用与 Android 系统之间的交互,允许 Mono 代码调用 Java 层功能。
libxamarin-app.so 应用入口的原生代码,用于启动托管环境。由 Xamarin 编译链生成,负责初始化应用程序并加载托管代码。
libmonosgen-2.0.so Mono 运行时的 JIT/AOT 支持库,负责执行 .NET 中间语言(IL)代码。根据编译选项,决定是进行即时编译(JIT)还是预编译(AOT)。
libmono-component-marshal-ilgen.so Mono 内部组件,支持托管与非托管代码间的互操作(例如 P/Invoke 和委托封送)。用于确保托管代码能够与原生代码无缝通信。
libaot-Mono.Android.dll.so Mono.Android.dll 的 AOT 编译版本,提供 Xamarin.Android 的平台桥接功能。负责处理应用与 Android 系统的互动,支持 Android 特定功能和 API。
libaot-Mono.Android.Runtime.dll.so Mono.Android.Runtime.dll 的 AOT 输出,负责管理 Android 应用程序的生命周期、上下文、和资源等。
libaot-Xamarin.AndroidX.*.dll.so AndroidX 支持库的 AOT 编译版本,提供对 AndroidX 组件(如RecyclerView、ViewModel、LiveData 等)的 AOT 编译支持。
libaot-Xamarin.Google.Android.Material.dll.so Xamarin 提供了对 Google Material Components for Android(MDC-Android)的绑定库,支持使用 Material Design 组件。
libaot-Xamarin.Kotlin.StdLib.dll.so Xamarin 对 Kotlin 标准库(kotlin-stdlib)的绑定 AOT 编译文件,支持 Kotlin 与 Xamarin 应用的集成。
libaot-Xamarin.KotlinX.Coroutines.Core.Jvm.dll.so Xamarin 对 Kotlin 协程核心库(kotlinx.coroutines)的绑定,并进行 AOT 编译后的产物,支持异步操作和并发编程。
libaot-Microsoft.Maui.*.dll.so MAUI 框架核心库(如 Microsoft.Maui.dll和Microsoft.Maui.Controls.dll)的 AOT 编译产物,提供 MAUI 特性和组件的 AOT 支持。
ibaot-CommunityToolkit.Maui.dll.so CommunityToolkit.Maui.dll 程序集的 AOT 编译版本,整合 Toolkit 的主逻辑模块。
libaot-CommunityToolkit.Maui.*.dll.so .NET MAUI Community Toolkit(.NET MAUI 社区工具包)中相关程序集(如 libaot-CommunityToolkit.Maui.Core.dll.so)的 AOT 编译版本,包含扩展、行为和高级 UI 控件等功能模块。

5.其他文件:有些文件是在构建 Android 应用时自动生成的,通常使用默认命名,没有被手动更改。

  • maui_splash.xml:MAUI 的默认启动画面布局文件;
  • maui_splash_image.xml:配置启动画面的图像资源;
  • microsoft_maui_essentials_fileprovider_file_paths.xml: 配置 Android FileProvider 可访问的文件路径

六、IOC

恶意文件(SHA1)

1
2
a5d8089d319962a6a4aede46f1c89bc52480978e  样本哈希
676b96cbfd33f97e0c9190d14e71b1b5e4c02335 Killer-2-Maui.dll

恶意IOC

1
120.27.233[.]135:1833

报告链接

分析报告:天穹沙箱分析报告

七、 技术支持与反馈

星图实验室深耕沙箱分析技术多年,致力于让沙箱更好用、更智能。做地表最强的动态分析沙箱,为每个样本分析人员提供便捷易用的分析工具,始终是我们追求的目标。各位同学在使用过程中有任何问题,欢迎联系我们。

code


天穹沙箱支持模拟14种CPU架构的虚拟机,环境数量50+,全面覆盖PC、服务器、智能终端、IoT设备的主流设备架构形态。在宿主机方面,除了Intel/AMD的x86架构CPU和CentOS操作系统之外,天穹沙箱支持海光、飞腾、鲲鹏等x86、ARM架构国产CPU和银河麒麟、中科方德等信创操作系统。

天穹沙箱系统以云沙箱、引擎输出、数据接口等多种形式服务于公司各个业务部门,包括天眼、终端安全、态势感知、ICG、锡安平台、安服等。

天穹公网地址:https://sandbox.qianxin.com
天穹内网地址(使用域账号登录):https://sandbox.qianxin-inc.cn