米米的博客

做了一点微小的工作

SDRPlay RSP1 是一款性价比较高的 SDR 接收机,支持频率范围从 1kHz 到 2GHz,适用于无线电接收、频谱分析、信号侦查等应用场景。笔者最近购买了一台进行研究,虽然是国产的改进版本,但主要芯片型号与原版的 RSP1 相同,因此可以使用同款的驱动和软件。然而,许多好用的上位机软件只支持 Windows 系统,在 Mac 上配置一个合适的环境还需要一番功夫。本文将介绍在 Mac 上使用 SDRPlay RSP1 的具体方法。

使用 SDRuno

SDRPlay 官方提供了丰富的软件支持,其中功能最完善的是 SDRuno,可惜只有 Windows 版本,而且由于驱动问题,需要使用虚拟机才能在 Mac 上运行。如果要使用 SDRuno,需要先通过 VMWare Fusion 或者 Parallels Desktop 等软件安装 Windows 系统,然后在 Windows 系统中安装 SDRuno。之后,将 SDRPlay RSP1 设备连接到 Mac 上,并通过 USB 透传到虚拟机中,即可识别到设备。

使用 SDR++

SDR++ 是一款开源的 SDR 软件,支持多种设备,包括 SDRPlay RSP1。它的界面简洁,功能强大,支持多种操作系统,包括 Windows、Linux 和 macOS。在 macOS 上使用 SDR++ 非常方便,只需要从 GitHub 仓库中下载对应的安装包,安装后即可使用。SDR++ 支持多种设备,可以在侧栏中的 Source 下拉菜单中选择。然而,在 macOS 上,默认安装好的 SDR++ 并不能识别到 SDRPlay RSP1。查看文档可以发现,支持 SDRPlay RSP1 需要安装 sdrplay_source 插件,这个插件在 Release 版本中是包含的,但它依赖于 SDRplay API,需要手动进行安装。

SDRplay API 是 SDRPlay 官方提供的一套 API,用于支持 SDRPlay 设备在第三方软件中的使用。它可以从 SDRPlay 官网上下载。下载好后,完成安装,相关资源会存放在 /usr/local/sdrplay//usr/local/lib/ 目录下。然而,此时再次启动 SDR++,发现仍然无法识别到 SDRPlay RSP1。这是因为 SDR++ 写死了加载动态库文件的地址,需要手动拷贝才可以:

1
cp /usr/local/lib/libsdrplay_api.so.3 /Applications/SDR++.app/Contents/Frameworks/

完成之后,再次启动 SDR++,即可在 Source 下拉菜单中看到 SDRPlay RSP1 设备。

SDR++中使用SDRPlay RSP1

需要注意,如果其它应用程序占用了设备,侧栏中会出现 No device available 的提示。此时需要关闭占用设备的应用程序,然后重新插拔设备,再重启 SDR++ 然后重新选择设备。

其它软件

除了重点介绍的 SDRuno 和 SDR++,还有一些其它软件也支持在 Mac 上使用 SDRPlay RSP1,例如 SDRconnectCubicSDR 等。SDRconnect 也是由 SDRPlay 官方提供的,相比 SDRuno 支持更多系统,可以方便的在 macOS 上使用。然而,它的功能相对较弱,缺少一些高级功能,并且界面存在一些 bug,本文撰写时最新的 1.0.4 版本在拖动部分子框体时会出现错位的问题,使用体验一般。CubicSDR 是一个开源软件,支持多种 SDR 设备,但是代码已经很久没有更新了。总的来说,SDR++ 是在 macOS 上使用 SDRPlay RSP1 的最佳选择。如果不介意使用虚拟机的话,SDRuno 也是一个不错的选择。

Proxmark3 是一款 RFID 工具,支持多种协议和频段,常用于 IC、ID 卡的研究、破解、仿制和安全测试等场景。Proxmark3 的硬件包括主控板和天线,主控板上有 ARM 架构的处理器和用于调制解调的 FPGA。软件则包含运行在 ARM 处理器上的固件和运行在计算机的客户端软件,两者需要版本匹配才能工作。在淘宝上有许多商家售卖 Proxmark3,且会附送软件。然而,笔者注意到,原版的 Proxmark3 软件是以 GPL 协议开源的,但卖家提供的软件通常只有二进制形式,而没有包含源代码,存在违反开源协议的嫌疑。为了支持开源事业,笔者直接从闲鱼上以较低的价格购买了 Proxmark3 硬件,并尝试自行刷写固件。

现在社区中较为活跃的 Proxmark3 软件是由 RfidResearchGroup 团队维护的,其仓库地址为 https://github.com/RfidResearchGroup/proxmark3。由于 Chris "Iceman" Herrmann 是主要的开发者,因此这一版本通常被称为 Iceman(冰人)版本。

Iceman 软件支持多种不同的 Proxmark3 硬件,包括 RDV4、Easy 等。在编译使用 Proxmark3 之前,需要先确认 Proxmark3 的硬件版本。如果硬件和软件版本不匹配,可能在烧录固件或者在使用时报错,此时重新编译软件并烧录正确的固件即可解决。不同版本的 Proxmark3 外观可参考下图,截取自 Proxmark3 网站。市面上比较常见的是 Proxmark3 Easy 版本。RDV4 版本功能更加强大,但是价格也更高,售价通常超过 1000 元。

不同的Proxmark3硬件

编译 Iceman 版本的 Proxmark3 软件

Iceman 软件可以在 Linux、Windows、macOS 等系统上编译和运行,本文以 Linux 为例。

首先,将仓库克隆到本地:

1
git clone https://github.com/RfidResearchGroup/proxmark3

然后进入目录,执行编译命令:

1
2
3
cd proxmark3
make clean
make all PLATFORM=PM3GENERIC

make all 默认的配置是编译兼容 RDV4 硬件版本的软件,然而现在市面上常见的 Proxmark3 都并不是这一硬件版本。因此,需要指定 PLATFORM 参数,将编译目标设置为 PM3GENERIC,也就是 Generic Proxmark3 platforms。

编译完成后,待烧录的固件将会生成在 recovery 目录下。client 目录下则会出现 proxmark3 可执行文件,这就是客户端软件,是一个命令行工具。仓库根目录下的 pm3 脚本会执行 proxmark3,本文后续的操作也都是基于这个工具进行的。如果要使用图形界面,需要单独下载额外的 GUI 程序。

阅读全文 »

在 macOS 上的「照片」App 中,通过智能相簿功能可以方便地管理照片。在创建智能相簿时,可以设定一系列规则,根据相机型号、拍摄日期等条件筛选照片,然后将这些照片添加到相册中。当相册中有新照片加入时,符合条件的照片也会自动添加到智能相簿中。

智能相簿

但是,目前智能相簿所支持的条件仍然有一定的局限性。例如,笔者想要筛选出来自 iPhone 6S 的照片,即使设置了相机型号规则,也还有大量的手机截屏没有被包含进来。造成问题的原因是手机截屏照片的 EXIF 中并不包含相机型号信息。如果需要筛选这些截屏,使用分辨率作为判断依据是更好的,可惜智能相簿目前还不支持对分辨率进行筛选。类似这样的需求还有很多,有没有办法通过自动化的方式进行处理呢?答案是肯定的,使用 Applescript 编程即可实现。

Applescript 是 macOS 系统自带的脚本语言,可以用于控制系统内置应用程序,实现自动化操作。对于「照片」App,Applescript 可以用于获取照片信息、创建相册、添加照片等操作。下面是一个简单的例子,用于筛选 iPhone 6S 的截屏照片,并将其添加到一个名为「iPhone 6S Screenshot」的相册中。

运行这段脚本的方法是,打开「脚本编辑器」App,将脚本粘贴到编辑器中,然后点击运行按钮。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
tell application "Photos"
log "Start..."

-- 创建一个新相册,如果相册已存在则使用现有相册
if not (exists album "iPhone 6S Screenshot") then
make new album named "iPhone 6S Screenshot"
end if
set targetAlbum to album "iPhone 6S Screenshot"

-- 定义目标分辨率
set targetWidth to 750
set targetHeight to 1334

-- 初始化图片列表
set imageList to {}

-- 获取所有照片
set allPhotos to every media item

-- 循环检查每张照片的分辨率
repeat with aPhoto in allPhotos
if width of aPhoto is targetWidth and height of aPhoto is targetHeight then
-- 打印照片的信息,例如文件名
log "Matching photo: " & filename of aPhoto & ", Date: " & date of aPhoto
-- 将符合分辨率的照片添加到列表
set end of imageList to aPhoto
end if
end repeat

-- 如果列表不为空,将照片添加到相册
if (count of imageList) > 0 then
-- 打印符合条件的照片数量
log "Number of matching photos: " & (count of imageList)
add imageList to targetAlbum
else
log "No matching photos found."
end if
end tell

如果一切顺利,脚本编辑器的输出窗口中会显示符合条件的照片信息。运行完成后,「照片」App 也会弹出提示,让用户确认操作。

这个例子只是一个简单的示例,实际应用中可以根据需求进行扩展,例如添加更多的筛选条件、自动化处理照片等。现有的大模型也可以帮助编写 Applescript 的代码。不过,在通过 Applescript 自动化操作其它 App 时,错误的代码逻辑可能会导致数据丢失,因此需要仔细检查代码并做好备份。

近期,笔者接到了维修一台屏幕完全碎裂的 iPad 的请求。根据机主描述,这台 iPad 在多年前由于从高处跌落,外屏完全损坏,无法开机,因此长期没有使用。但是里面有一些重要的照片,希望能够取出来。

经过检查,这台 iPad 型号为 iPad Mini 1(A1432 版本)。机器状况确实非常不理想,不仅屏幕碎裂,后壳也有明显的变形。只能祈祷主板没有受损,不然就超出笔者的维修能力了。

变形的机身和内屏,外屏由于完全碎裂爆出了很多玻璃渣,已经丢弃

这一代 iPad 的外屏是通过胶水粘合的,正常的拆卸方法是加热后将屏幕揭开,但由于外屏已经完全脱落,这一步骤甚至可以省略。进一步拆卸可以参考 iFixit 发布的指南,并不麻烦,主要就是拧螺丝和拆排线,谨慎操作即可,避免损坏一些脆弱的部件。经过拆卸,这台机器的主板外观还比较完好,变形也比较轻微,看上去还能抢救一下。但是,除了主板外,其它很多硬件都存在不同程度的受损。

拆卸出的主板很完整,但电池已经变形了

相比于单独购买屏幕、电池等配件进行替换,更方便的方法是直接买一台好的 iPad,来一手借尸还魂,把坏机器的主板挪过去。在闲鱼上,同款的 iPad Mini 1 已经是白菜价格,外观成色完美的也就大几十块钱,因此这个方案是经济可行的。

硬件维修的部分还算顺利,经过拆机和更换硬件,这台 iPad Mini 1 终于能够正常开机了。

拆机过程

点亮设备

接下来才是真正有趣的部分:这台 iPad 设置了锁屏密码,但是由于多年没有开机,机主已经忘记了密码。如果要将其中的数据备份出来,就得想办法访问到 iPad 的文件系统了。如果设备在越狱后开启了 SSH 服务,我们可以通过 libimobiledevice 提供的 usbmuxd 转发端口,使用 SSH 连接来访问文件系统。但遗憾的是,经过简单的实验,这台 iPad 似乎并没有越狱,SSH 服务也没有启动,这条路行不通。

由于 iPad Mini 1 是非常古早的设备,想必是有很多现成的 n-day 漏洞可以利用。其中,最著名的就是 2019 年由 iOS 安全研究员 axi0mX 披露的 checkm8 漏洞(这个缩写类似 DotA2 中的 str8,8=eight 是读音的缩写,原意即为 checkmate)。checkm8 漏洞存在于 iOS 设备的 ROM 中,通过 USB 发送恶意的 Payload,可以实现任意代码执行,绕过设备的安全启动限制。这个漏洞影响了搭载 A5 到 A11 芯片的苹果设备,并且苹果无法通过软件更新来修复这个漏洞,非常适合我们的场景。不过,由于 checkm8 漏洞利用需要通过 USB 进行,目前针对 A5 芯片最成熟的方案是使用 Arduino USB Host Shield,这需要我们准备特殊的硬件。

阅读全文 »

CVE-2023-21537 是一个 Windows 消息队列(MSMQ)驱动程序 mqac.sys 中的漏洞。该漏洞于 2023 年 1 月披露,并已被微软修复。漏洞并没有公开的 PoC 程序,漏洞发现者只通过文章 Racing bugs in Windows kernel 透露了部分信息。笔者在其基础上深入分析了相关代码,成功地复现了此漏洞。本文就是对漏洞研究的总结。

漏洞复现环境搭建

由于此漏洞已经在较新的系统中被修复,复现漏洞需要在旧版本的系统中进行。笔者在 Hyper-V 虚拟机中安装了 Windows 10 21H1 版本,内部版本号为 19043.928。此外,由于消息队列是 Windows 的可选功能,需要在控制面板中手动启用。方法是:打开控制面板,选择「程序」,点击「启用或关闭 Windows 功能」,并开启「Microsoft 消息队列(MSMQ)服务器」。如下图所示。

启用消息队列

之后,打开计算机管理,在侧栏中选择「服务和应用程序」,点开「消息队列」下面的「专用队列」,并右键新建专用队列。队列名称需要记下来,之后的 PoC 程序里面需要用到。完成后,情况如下图所示。

新建专用队列

为了方便调试 PoC 程序,在虚拟机中还可以开启内核调试,这样就可以通过 Host 上的 WinDbg 给虚拟机的 Windows 内核下断点进行调试了。方法是开启管理员权限的 Powershell,执行如下命令

1
2
bcdedit /debug on
bcdedit /dbgsettings net hostip:192.168.1.2 port:50000 key:1.2.3.4

hostip 换成 Host 的 ip 地址(实际情况下即使 ip 改变也不影响),portkey 可以自定义设置。完成后,在 WinDbg 里面开启内核调试器,输入 Hyper-V 的虚拟交换机分配给 Guest 的 ip 地址,以及对应的 portkey 参数,之后重启虚拟机,就可以对虚拟机的内核进行 debug 了。

漏洞分析

根据漏洞发现者的文章,该漏洞的成因是 mqac.sys 中的 ACSendMessage 函数会两次读取一个来自用户的输入参数,第一次该参数用于控制数组长度,第二次则是在释放堆内存时,根据该长度(也就是数组中的元素数量)进行资源释放。然而,这段逻辑并未考虑参数会被用户修改的可能,因而构成一个 Double fetch 漏洞,可能导致错误的内存被释放。

mqac.sys 提供了 IoControl 调用的处理函数,名称为 ACDeviceControl,该函数将会解析用户传入的参数,并调用不同的派发函数。通过逆向分析 ACDeviceControl 函数,发现当 IoControl 调用号为 0x19658107 且输出缓冲区的总长度为 0x2C0 时,它会进一步调用 ACSendMessage 这一派发函数。IDA 反编译得到的关键代码如下(省略了部分上下文):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
__int64 __fastcall ACDeviceControl(struct _DEVICE_OBJECT *DeviceObject, struct _IRP *Irp)
{
struct _IO_STACK_LOCATION *CurrentStackLocation Irp->Tail.Overlay.CurrentStackLocation;
unsigned int LowPart = CurrentStackLocation->Parameters.Read.ByteOffset.LowPart;
__int64 Length = CurrentStackLocation->Parameters.Read.Length;
const struct CQueueBase *FsContext = CurrentStackLocation->FileObject->FsContext;
NTSTATUS Information;
// ...
switch ( LowPart )
{
case 0x19658107u:
if ( (_DWORD)Length == 704 )
{
Information = ACSendMessage(DeviceObject, Irp, Options, FsContext, (struct CACSendParameters *)UserBuffer);
goto LABEL_246;
}
// ...
}
// ...
}

ACSendMessage 函数首先将用户态缓冲区复制到内核态栈上的缓冲区,之后,将执行核心的业务逻辑,调用 CQueue::PutNewPacket 来发送用户请求的数据,完成后再调用 ACFreeDeepCopyQueueFormat 进行堆内存的释放。ACFreeDeepCopyQueueFormat 的第一个参数是数组的头指针,第二个参数是元素数量。该函数会根据元素数量循环释放数组中的资源。此处便存在漏洞:调用 ACFreeDeepCopyQueueFormat 时传入的第二个参数直接读取自用户态缓冲区中,它可能已经被篡改,使得驱动错误地判断元素个数。下面是 IDA 反编译得到的关键结果,参数 UserBuffer 就是指向用户缓冲区的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
__int64 __fastcall ACSendMessage(
struct _DEVICE_OBJECT *DeviceObject,
struct _IRP *Irp,
unsigned int Options,
const struct CQueueBase *FsContext,
struct CACSendParameters *UserBuffer)
{
const struct BOID *v15;
PVOID Contents[36];
memset(Contents, 0, 0x118ui64);
_DWORD *DeviceExtension = DeviceObject->DeviceExtension;
int v9 = CQueueBase::Validate(FsContext);
if ( v9 >= 0 )
{
if ( DeviceExtension[246] )
{
ACDeepProbeSendParams(DeviceObject, UserBuffer, (struct ACSendParametersPointerContents *)Contents);
v15 = (const struct BOID *)*((_QWORD *)UserBuffer + 45);
}
// ...
if ( Contents[23] )
ACFreeDeepCopyQueueFormat((char *)Contents[23], *((_DWORD *)UserBuffer + 148));
// ...
}
// ...
}

这样问题看上去就很明确了:我们首先获得指向 MQAC 虚拟设备的句柄,然后向其发送号码为 0x19658107 的 IoControl 调用,并通过调整参数使 ACFreeDeepCopyQueueFormat 函数能够执行。这又要满足 Contents[23] 不为零的约束条件。观察之后发现 Contents 先被初始化为了全 0,然后传入了 ACDeepProbeSendParams 这个函数里面,所以还要设法让这个函数帮我们修改 Contents[23],才能触发漏洞。

阅读全文 »
0%