试用Sandboxie-Plus高级功能的另一种思路
本文章仅用于技术交流和方便安全分析测试使用. Sandboxie-Plus的作者开通了爱发电, 一个月就8块钱, 很便宜.
测试环境: windows server 2025 Github: b61K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6q4c8f1g2q4K9r3g2^5i4K6u0r3f1$3u0A6k6f1E0W2M7X3&6W2L8q4m8S2N6r3y4Z5
前言
拜读了baolongshou的文章[基于BYOVD方法无限使用SandboxiePlus的高级功能 ]后,发现他Patch的点既需要修改内核层的驱动数据, 又需要劫持用户层dll, 这样每次更新都需要替换dll.
因此, 基于他的工作, 我提供了另一种Patch思路, 只需修改内核层的驱动数据.
思路
1. Patch公钥
通过阅读源码[Sandboxie>Core>drv>verify.c]可知, 验证注册信息的函数是:
1
2
3
4
5
6
7
8
9
10
11
_FX NTSTATUS KphValidateCertificate()
{
BOOLEAN CertDbg = FALSE;
static const WCHAR *path_cert = L"%s\\Certificate.dat";
NTSTATUS status;
ULONG path_len = 0;
WCHAR *path = NULL;
STREAM *stream = NULL;
...
}
大致逻辑就是:
读取运行目录下的Certificate.dat文件
然后按行开始解析, 将key:value中(去掉:)的key和value做hash
读取SIGNATURE这个key字段的字符串数据, 然后解base64, 拿到签名数据
将hash和sig进行签名校验
签名校验的逻辑在:
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
39
NTSTATUS KphVerifySignature(
_In_ PVOID Hash,
_In_ ULONG HashSize,
_In_ PUCHAR Signature,
_In_ ULONG SignatureSize
)
{
NTSTATUS status;
BCRYPT_ALG_HANDLE signAlgHandle = NULL;
BCRYPT_KEY_HANDLE keyHandle = NULL;
PVOID hash = NULL;
ULONG hashSize;
// Import the trusted public key.
if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&signAlgHandle, KPH_SIGN_ALGORITHM, NULL, 0)))
goto CleanupExit;
if (!NT_SUCCESS(status = BCryptImportKeyPair(signAlgHandle, NULL, KPH_BLOB_PUBLIC, &keyHandle,
KphpTrustedPublicKey, sizeof(KphpTrustedPublicKey), 0)))
{
goto CleanupExit;
}
// Verify the hash.
if (!NT_SUCCESS(status = BCryptVerifySignature(keyHandle, NULL, Hash, HashSize, Signature,
SignatureSize, 0)))
{
goto CleanupExit;
}
CleanupExit:
if (keyHandle)
BCryptDestroyKey(keyHandle);
if (signAlgHandle)
BCryptCloseAlgorithmProvider(signAlgHandle, 0);
return status;
}
其中KPH_SIGN_ALGORITHM这个宏为:
使用的是椭圆曲线ECDSA签名校验, 公钥位于全局变量KphpTrustedPublicKey中, 因此 , 既然是做的签名校验, 那直接把公钥Patch成自己的公钥, 然后拿自己的私钥做签名就好了, 这样即使任何时候用户层请求了重载证书验证也无所谓了.
2. 重新生成SandMan.exe.sig
在[SandboxiePlus>SandMan>SandMan.cpp]中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SB_STATUS CSandMan::ConnectSbieImpl()
{
SB_STATUS Status = theAPI->Connect(g_PendingMessage.isEmpty(), theConf->GetBool("Options/UseInteractiveQueue", true));
if (!g_PendingMessage.isEmpty()) {
OnMessage(g_PendingMessage);
PostQuitMessage(0);
}
if (Status.GetStatus() == 0xC0000038L /*STATUS_DEVICE_ALREADY_ATTACHED*/) {
OnLogMessage(tr("CAUTION: Another agent (probably SbieCtrl.exe) is already managing this Sandboxie session, please close it first and reconnect to take over."));
Status = SB_OK;
}
else if (Status.GetStatus() == 0xC000A000L /*STATUS_INVALID_SIGNATURE*/) {
QMessageBox::critical(this, "Sandboxie-Plus", tr("<b>ERROR:</b> The Sandboxie-Plus Manager (SandMan.exe) does not have a valid signature (SandMan.exe.sig). Please download a trusted release from the <a href=\"620K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6j5h3&6V1j5X3!0^5K9h3g2Q4x3X3c8H3L8s2g2K6i4K6u0W2j5$3!0E0i4K6u0r3k6$3!0Q4x3X3g2H3K9s2m8Q4x3@1k6@1L8#2)9K6c8s2y4T1K9h3g2Q4x3X3c8Y4k6i4c8Q4y4f1x3`. ">official Download page</a>."));
Status = SB_OK;
}
return Status;
}
进程启动的时候会请求驱动给SandMan.exe做一次签名, 然后对比SandMan.exe.sig里的签名数据, 不一致则弹窗, 但这个并不影响功能, 要去掉弹窗只需要拿到SandMan.exe的hash后拿自己的私钥重新签名后替换SandMan.exe.sig即可.
3. 总体逻辑
使用WIN API生成一对公钥私钥skp_public_key.blob/skp_private_key.blob
生成证书内容Certificate.dat, 计算完hash后用skp_private_key.blob私钥进行签名, base64编码后将SIGNATURE字段添加到证书末尾
安装ehco.sys驱动, 用于内核层内存读写
获取SbieDrv.sys的基址
通过SbieDrv.pdb获取变量KphpTrustedPublicKey的偏移
将KphpTrustedPublicKey处内存Patch为skp_public_key.blob的内容
计算SandMan.exe的hash以及签名后替换到SandMan.exe.sig中
卸载echo.sys驱动
Enjoy!
4. 代码编写
具体细节请查看github
文件架构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Header
├── Echo 驱动接口文件
│ ├── DriverInterface.h Echo驱动接口
│ └── DriverTypes.h Echo驱动使用的数据结构
│
├── Third
│ └── args.hxx 命令行解析文件
│
├── DriverUtil.h 驱动控制文件(安装\启动\停止\卸载)
├── SbieUtil.h 沙箱相关文件(获取HWID, 获取pdb中偏移等)
├── SigUtil.h 数字签名相关文件
└── Utils.h 工具函数
Source
├── main.cpp
└── SigUtil.cpp 签名逻辑实现
签名相关的实现都是Cursor写的, 大模型真是方便啊.
效果
默认生成ETERNAL类型证书, 永不过期, 功能全开:
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!