windows容器基于两种机制实现,一个使用虚拟化技术将container放在hypvervirsor中执行,另一个是使用叫做server silo的技术利用内核提供的namespace隔离机制。本文只分析server silo的实现机制,基于win10专业版10.0.19045进行逆向工程分析。
微软公司未提供server silo的用户态接口信息,内核接口可以在ntddk.h中找到,这意味着微软不希望用户使用容器技术,只有微软公司自身才能使用。
Server silo基于windows job来实现,首先建立一个job object,然后使用SetInformationJobObject函数启动silo,可以在winnt.h中看到以下定义:
typedef enum _JOBOBJECTINFOCLASS {
...
JobObjectCreateSilo=35,
JobObjectSiloBasicInformation,
JobObjectReserved15Information = 37,
JobObjectReserved16Information = 38,
JobObjectReserved17Information = 39,
JobObjectReserved18Information = 40,
JobObjectReserved19Information = 41,
JobObjectReserved20Information = 42,
JobObjectReserved21Information = 43,
JobObjectReserved22Information = 44,
JobObjectReserved23Information = 45,
JobObjectReserved24Information = 46,
JobObjectReserved25Information = 47,
MaxJobObjectInfoClass
} JOBOBJECTINFOCLASS;
黄色部分是silo要使用的值,但是虽然在头文件中找到了JobObjectSiloBasicInformation的定义,但是在笔者的win10专业版本的内核中并未提供此接口的实现,猜测可能在win10 server中有相关实现。
typedef struct _SILOOBJECT_BASIC_INFORMATION {
DWORD SiloId;
DWORD SiloParentId;
DWORD NumberOfProcesses;
BOOLEAN IsInServerSilo;
BYTE Reserved[3];
} SILOOBJECT_BASIC_INFORMATION, *PSILOOBJECT_BASIC_INFORMATION;
typedef enum _SERVERSILO_STATE {
SERVERSILO_INITING = 0,
SERVERSILO_STARTED,
SERVERSILO_SHUTTING_DOWN,
SERVERSILO_TERMINATING,
SERVERSILO_TERMINATED,
} SERVERSILO_STATE, *PSERVERSILO_STATE;
typedef struct _SERVERSILO_BASIC_INFORMATION {
DWORD ServiceSessionId;
SERVERSILO_STATE State;
DWORD ExitStatus;
} SERVERSILO_BASIC_INFORMATION, *PSERVERSILO_BASIC_INFORMATION;
这个接口的相关顺序如下:
1、首先使用35调用PspCreateSilo分配一个silo storage并设置silo标志。
2、使用37建立silo的root directory和相关namespace空间。
3、使用40进一步做silo的初始化。
4、使用41结束silo服务。
5、使用47控制silo服务状态。
lkd> dt nt!_eprocess
+0x464 BreakOnTermination : Pos 13, 1 Bit
+0x510 Job : Ptr64 _EJOB
lkd> dt nt!_ejob
+0x430 ParentJob : Ptr64 _EJOB
+0x438 RootJob : Ptr64 _EJOB
+0x4d4 JobId : Uint4B
+0x4d8 ContainerId : _GUID
+0x4e8 ContainerTelemetryId : _GUID
+0x4f8 ServerSiloGlobals : Ptr64 _ESERVERSILO_GLOBALS
+0x500 PropertySet : _PS_PROPERTY_SET
+0x518 Storage : Ptr64 _PSP_STORAGE
+0x528 Silo : Pos 30, 1 Bit
+0x528 ContainerTelemetryIdSet : Pos 31, 1 Bit
+0x52c JobFlags2 : Uint4B
+0x52c ParentLocked : Pos 0, 1 Bit
+0x52c EnableUsermodeSiloThreadImpersonation : Pos 1, 1 Bit
+0x52c DisallowUsermodeSiloThreadImpersonation : Pos 2, 1 Bit
+0x5f0 SiloHardReferenceCount : Int8B
_ESERVERSILO_GLOBALS结构保存silo的状态信息。
1: kd> dt nt!_ESERVERSILO_GLOBALS
+0x000 ObSiloState : _OBP_SILODRIVERSTATE
+0x2e0 SeSiloState : _SEP_SILOSTATE
+0x310 SeRmSiloState : _SEP_RM_LSA_CONNECTION_STATE
+0x360 EtwSiloState : Ptr64 _ETW_SILODRIVERSTATE
+0x368 MiSessionLeaderProcess : Ptr64 _EPROCESS
+0x370 ExpDefaultErrorPortProcess : Ptr64 _EPROCESS
+0x378 ExpDefaultErrorPort : Ptr64 Void
+0x380 HardErrorState : Uint4B
+0x388 ExpLicenseState : Ptr64 _EXP_LICENSE_STATE
+0x390 WnfSiloState : _WNF_SILODRIVERSTATE
+0x3c8 DbgkSiloState : _DBGK_SILOSTATE
+0x3e8 PsProtectedCurrentDirectory : _UNICODE_STRING
+0x3f8 PsProtectedEnvironment : _UNICODE_STRING
+0x408 ApiSetSection : Ptr64 Void
+0x410 ApiSetSchema : Ptr64 Void
+0x418 OneCoreForwardersEnabled : UChar
+0x419 TzVirtualizationSupported : UChar
+0x420 ImgFileExecOptions : Ptr64 Void
+0x428 ExTimeZoneState : Ptr64 _EX_TIMEZONE_STATE
+0x430 NtSystemRoot : _UNICODE_STRING
+0x440 SiloRootDirectoryName : _UNICODE_STRING
+0x450 Storage : Ptr64 _PSP_STORAGE
+0x458 State : _SERVERSILO_STATE
+0x45c ExitStatus : Int4B
+0x460 DeleteEvent : Ptr64 _KEVENT
+0x468 UserSharedData : Ptr64 _SILO_USER_SHARED_DATA
+0x470 UserSharedSection : Ptr64 Void
+0x478 TerminateWorkItem : _WORK_QUEUE_ITEM
+0x498 IsDownlevelContainer : UChar
建立silo的storage结构。
PAGE:00000001405D3FAC PspCreateSilo proc near ; CODE XREF: NtSetInformationJobObject+1A1C↓p
PAGE:00000001405D3FAC
PAGE:00000001405D3FD2 call PsIsCurrentThreadInServerSilo
PAGE:00000001405D3FD7 test al, al
PAGE:00000001405D3FD9 jz short loc_1405D3FE5
PAGE:00000001405D3FDB mov eax, 0C0000061h
PAGE:00000001405D3FE0 jmp loc_1405D4088
如果当前进程已经有一个silo,则直接返回。
PAGE:00000001405D3FE5 cmp [rbx+518h], rdi
PAGE:00000001405D3FEC jnz short loc_1405D4005
PAGE:00000001405D3FEE lea rcx, [rsp+28h+arg_8]
PAGE:00000001405D3FF3 call PspAllocStorage
判断ejob->Storage如果为空则调用PspAllocStorage分配一个silo context。
PAGE:00000001405D4013 call PspJobHasChildren
PAGE:00000001405D4018 test al, al
PAGE:00000001405D401A jz short loc_1405D4023
PAGE:00000001405D401C mov esi, 0C000050Fh
如果job存在子进程,设置esi为0C000050Fh。
PAGE:00000001405D4023 loc_1405D4023: ; CODE XREF: PspCreateSilo+6E↑j
PAGE:00000001405D4023 mov edx, 40000000h
PAGE:00000001405D4028 test [rbx+528h], edx
PAGE:00000001405D402E jz short loc_1405D4037
PAGE:00000001405D4030 mov esi, 0C0000508h
如果ejob->Silo == 1,设置esi为0C0000508h。
PAGE:00000001405D404A loc_1405D404A: ; CODE XREF: PspCreateSilo+95↑j
PAGE:00000001405D404A xor eax, eax
PAGE:00000001405D404C lock cmpxchg [rbx+518h], rdi
将当前ejob->Storage值与PspAllocStorage分配的地址互换。
接着看下PspAllocStorage的实现:
PAGE:000000014064EF40 ; __int64 __fastcall PspAllocStorage(_QWORD *)
PAGE:000000014064EF40 PspAllocStorage proc near
PAGE:000000014064EF46 mov edx, 240h ; NumberOfBytes
PAGE:000000014064EF4B mov rbx, rcx
PAGE:000000014064EF4E mov r8d, 74537350h ; Tag
PAGE:000000014064EF54 lea ecx, [rdx-3Ch] ; PoolType
PAGE:000000014064EF57 call ExAllocatePoolWithTag
微软符号表中并没有给出_PSP_STORAGE结构体的定义。
lkd> dt nt!_PSP_STORAGE
Symbol nt!_PSP_STORAGE not found.
但是根据传递给ExAllocatePoolWithTag函数的第二个参数为240h,所以可以断定_PSP_STORAGE结构体大小为240h。
PAGE:000000014064EF67 lea ecx, [r8+20h]
PAGE:000000014064EF6B
PAGE:000000014064EF6B loc_14064EF6B: ; CODE XREF: PspAllocStorage+3A↓j
PAGE:000000014064EF6B mov [rax], r8
PAGE:000000014064EF6E mov [rax+8], r8
PAGE:000000014064EF72 lea rax, [rax+10h]
PAGE:000000014064EF76 sub rcx, 1
PAGE:000000014064EF7A jnz short loc_14064EF6B
PAGE:000000014064EF7C mov [rdx+200h], r8
ecx设置为32,将storage地址依次设为0,可以试着组建一下_PSP_STORAGE结构体:
struct _PSP_STORAGE {
slot_type slots[32];
...
}
struct slot_type {
void *ptr1;
void *ptr2;
}
启用或关闭silo,它有两个参数第一个为ejob结构,第2个为silo的状态值。
AGE:0000000140909864 PspSetJobSiloThreadImpersonationPolicy proc near
PAGE:0000000140909864 mov eax, 2
PAGE:0000000140909869 mov r9, rcx
PAGE:000000014090986C cmp edx, eax
PAGE:000000014090986E lea r8d, [rax+2]
PAGE:0000000140909872 cmovnz r8d, eax
PAGE:0000000140909876 mov eax, [rcx+52Ch]
PAGE:000000014090987C test edx, eax
PAGE:000000014090987E jz short loc_140909895
PAGE:0000000140909880
PAGE:0000000140909880 loc_140909880: PAGE:0000000140909880 mov al, 1
PAGE:0000000140909882 retn
如果第二个参数不为2,强行设为2,如果ejob->EnableUsermodeSiloThreadImpersonation已经置位则返回。
PAGE:0000000140909884
PAGE:0000000140909884 loc_140909884:
PAGE:0000000140909884 mov ecx, eax
PAGE:0000000140909886 or ecx, edx
PAGE:0000000140909888 lock cmpxchg [r9+52Ch], ecx
PAGE:0000000140909891 cmp eax, ecx
PAGE:0000000140909893 jz short loc_140909880
PAGE:0000000140909895
PAGE:0000000140909895 loc_140909895: ; CODE XREF: PspSetJobSiloThreadImpersonationPolicy+1A↑j
PAGE:0000000140909895 test eax, r8d
PAGE:0000000140909898 jz short loc_140909884
PAGE:000000014090989A xor al, al
PAGE:000000014090989C retn
设置ejob->EnableUsermodeSiloThreadImpersonation |= 2。
PAGE:00000001407C0E70 ; NTSTATUS __fastcall ObInitServerSilo(__int64)
PAGE:00000001407C0E70 ObInitServerSilo proc near
PAGE:00000001407C0E9D and qword ptr [rax+78h], 0 ; _ESERVERSILO_GLOBALS->ObSiloState->DeviceMapLock = 0
PAGE:00000001407C0EA2 lea rdx, [rax+80h] ; _ESERVERSILO_GLOBALS->ObSiloState->PrivateNamespaceLookupTable
PAGE:00000001407C0EA9 and qword ptr [rdx+250h], 0 ; _ESERVERSILO_GLOBALS->ObSiloState->PrivateNamespaceLookupTable->Lock = 0
PAGE:00000001407C0EB1 mov eax, 25h ; '%'
PAGE:00000001407C0EB6
PAGE:00000001407C0EB6 loc_1407C0EB6: ; CODE XREF: ObInitServerSilo+55↓j
PAGE:00000001407C0EB6 mov [rdx+8], rdx
PAGE:00000001407C0EBA mov [rdx], rdx
PAGE:00000001407C0EBD add rdx, 10h
PAGE:00000001407C0EC1 sub rax, 1
PAGE:00000001407C0EC5 jnz short loc_1407C0EB6
PAGE:00000001407C0EC7 call PsIsHostSilo
PAGE:00000001407C0ECC test al, al
PAGE:00000001407C0ECE jz loc_14085E746
初始化ejob->ObSiloState结构,ObSiloState->PrivateNamespaceLookupTable是一个哈希表,每个项是一个双向链表节点,blink,flink都指向自己。
PAGE:000000014085E753 call PsGetPermanentSiloContext
ejob->0x518地址保存silo context。
设置进程的enviorment内容
PAGE:00000001407BC430 ; __int64 __fastcall PspInitializeProtectedProcessParameters(__int64)
PAGE:00000001407BC430 PspInitializeProtectedProcessParameters proc near
PAGE:00000001407BC44F movdqu xmmword ptr [rcx+3E8h], xmm0 ; _ESERVERSILO_GLOBALS->PsProtectedCurrentDirectory = _ESERVERSILO_GLOBALS->NtSystemRoot
PAGE:00000001407BC457 movzx eax, word ptr [rcx+430h]
首先设置_ESERVERSILO_GLOBALS->PsProtectedCurrentDirectory
PAGE:00000001407BC457 movzx eax, word ptr [rcx+430h]
PAGE:00000001407BC45E movups xmm1, xmmword ptr [rcx+430h]
PAGE:00000001407BC465 add ax, 2Ah ; '*'
PAGE:00000001407BC469 add ax, ax
PAGE:00000001407BC46C mov [rcx+3F8h], ax ; PsProtectedEnvironment = (NtSystemRoot + 0x2a) * 2
PAGE:00000001407BC473 add ax, 2
PAGE:00000001407BC477 movzx edx, ax ; NumberOfBytes
PAGE:00000001407BC47A mov [rcx+3FAh], dx
PAGE:00000001407BC481 mov ecx, 1 ; PoolType
PAGE:00000001407BC486 movdqu [rsp+38h+var_18], xmm1
PAGE:00000001407BC48C call ExAllocatePoolWithTag ; (1, NumberOfBytes)
PAGE:00000001407BC491 xor esi, esi
PAGE:00000001407BC493 mov [rdi+400h], rax ; PsProtectedEnvironment + 8
PAGE:00000001407BC49A mov rbx, rax
PAGE:00000001407BC49D test rax, rax
PAGE:00000001407BC4A0 jz loc_14085D110
PAGE:00000001407BC4A6 mov rax, cs:off_140983D28 ; "Path="
PAGE:00000001407BC4AD movsd xmm0, qword ptr [rax]
PAGE:00000001407BC4B1 movsd qword ptr [rbx], xmm0
PAGE:00000001407BC4B5 movzx eax, word ptr [rax+8]
PAGE:00000001407BC4B9 mov [rbx+8], ax
PAGE:00000001407BC4BD add rbx, 0Ah
PAGE:00000001407BC4C1 movzx r8d, word ptr [rdi+430h] ; Size
PAGE:00000001407BC4C9 mov rcx, rbx ; void *
PAGE:00000001407BC4CC mov rdx, [rdi+438h] ; Src
PAGE:00000001407BC4D3 call memmove ; Path=NtSystemRoot+8
PAGE:00000001407BC4D8 mov rax, cs:off_140983D18 ; "\\System32"
PAGE:00000001407BC4DF movzx edx, word ptr [rdi+430h] ; NtSystemRoot->Length
PAGE:00000001407BC4E6 movups xmm0, xmmword ptr [rax]
PAGE:00000001407BC4E9 movups xmmword ptr [rdx+rbx], xmm0
PAGE:00000001407BC4ED movzx eax, word ptr [rax+10h]
PAGE:00000001407BC4F1 mov [rdx+rbx+10h], ax
PAGE:00000001407BC4F6 mov [rdx+rbx+12h], si
PAGE:00000001407BC4FB mov rax, cs:off_140983D38 ; "SystemDrive="
PAGE:00000001407BC502 movups xmm0, xmmword ptr [rax]
PAGE:00000001407BC505 movups xmmword ptr [rdx+rbx+14h], xmm0
PAGE:00000001407BC50A movsd xmm1, qword ptr [rax+10h]
PAGE:00000001407BC50F mov rax, qword ptr [rsp+38h+var_18+8] ; NtSystemRoot->Buffer
PAGE:00000001407BC514 movsd qword ptr [rdx+rbx+24h], xmm1
PAGE:00000001407BC51A mov ecx, [rax]
PAGE:00000001407BC51C mov [rdx+rbx+2Ch], ecx
PAGE:00000001407BC520 mov [rdx+rbx+30h], si
PAGE:00000001407BC525 mov rcx, cs:off_140983D48 ; "SystemRoot="
PAGE:00000001407BC52C movups xmm0, xmmword ptr [rcx]
PAGE:00000001407BC52F movups xmmword ptr [rdx+rbx+32h], xmm0
PAGE:00000001407BC534 mov eax, [rcx+10h]
PAGE:00000001407BC537 mov [rdx+rbx+42h], eax
PAGE:00000001407BC53B movzx eax, word ptr [rcx+14h]
PAGE:00000001407BC53F mov [rdx+rbx+46h], ax
PAGE:00000001407BC544 add rbx, rdx
PAGE:00000001407BC547 movzx r8d, word ptr [rdi+430h] ; Size
PAGE:00000001407BC54F mov rdx, [rdi+438h] ; Src
PAGE:00000001407BC556 lea rcx, [rbx+48h] ; void *
PAGE:00000001407BC55A call memmove ; NtSystemRoot
构造PsProtectedEnvironment内容:
+-------------------------------------------------------------------------------------------------------------------+
| Path= | NtSystemRoot->Buffer| \\System32 | SystemDrive= | NtSystemRoot->Buffer[0]|SystemRoot=|NtSystemRoot->Buffer |
+-------------------------------------------------------------------------------------------------------------------+
一共3个环境变量: Path、SystemDrive、SystemRoot
PAGE:0000000140906AF0 ; NTSTATUS __fastcall PspSiloInitializeSystemRootSymlink(_LIST_ENTRY *)
PAGE:0000000140906AF0 PspSiloInitializeSystemRootSymlink proc nea
PAGE:0000000140906B8F call RtlAppendUnicodeStringToString ; \\GLOBAL??\\ + _ESERVERSILO_GLOBALS->NtSystemRoot
PAGE:0000000140906B94 lea rax, PspSystemRootSymlinkName ; \\SystemRoot
PAGE:0000000140906BC1 lea r9, [rbp+DestinationString]
PAGE:0000000140906BC5 mov edx, 0F0001h
PAGE:0000000140906BCA lea r8, [rbp+var_30]
PAGE:0000000140906BCE mov rbx, rax
PAGE:0000000140906BD1 lea rcx, [rbp+Handle]
PAGE:0000000140906BD5 call ZwCreateSymbolicLinkObject
调用未公开接口ZwCreateSymbolicLinkObject建立\\SystemRoot到\\GLOBAL??\\ + _ESERVERSILO_GLOBALS->NtSystemRoot的软链接?
PAGE:0000000140906C1C ; __int64 __fastcall PspSiloInitializeUserSharedData(_LIST_ENTRY *)
PAGE:0000000140906C1C PspSiloInitializeUserSharedData proc near
PAGE:0000000140906CB3 mov [rax+468h], rcx ; _ESERVERSILO_GLOBALS->serSharedData = MappedBase
PAGE:0000000140906CBA mov rcx, rsi
PAGE:0000000140906CBD mov [rax+470h], rbx ; _ESERVERSILO_GLOBALS->UserSharedSection = Section
设置silo的serSharedData和UserSharedSection字段。
PspSiloInitializeLicenseData函数初始化silo的License Data
.text:00000001405B03A8 ; __int64 __fastcall PspSiloInitializeLicenseData(_LIST_ENTRY *)
.text:00000001405B03A8 PspSiloInitializeLicenseData proc near
.text:00000001405B0406 mov edx, 0B7C0h
.text:00000001405B040B mov ecx, 100h
.text:00000001405B0410 mov r8d, 69534C53h
.text:00000001405B0416 call ExAllocatePool2 ; struct _EXP_LICENSE_STATE
分配struct _EXP_LICENSE_STATE结构体,微软符号表中未提供它的数据结构。
.text:00000001405B049F mov [rbx+388h], rsi ; ejob->ExpLicenseState
.text:00000001405B04A6 mov [rbp+57h+var_68], 100h
.text:00000001405B04AD mov [rbp+57h+var_50], 3000003h
.text:00000001405B04B4 mov [rbp+57h+var_40], 14000h
.text:00000001405B04BB mov [rbp+57h+var_58], rsi
.text:00000001405B04BF call ExpInitLicensing
ExpInitLicensing 做简单初始化,如果是全局silo变量PspHostSiloGlobals,使用ExpHostBootLicensingData作为默认的license data。
.text:00000001405B04D7 lea rdx, aProductoptions_0 ; "ProductOptions"
.text:00000001405B04DE xor r9d, r9d
.text:00000001405B04E1 mov rbx, [rcx+608h]
.text:00000001405B04E8 mov [rcx+608h], rdi
.text:00000001405B04EF lea ecx, [r9+2]
.text:00000001405B04F3 call RtlQueryRegistryValuesEx ; (gs:188h, "ProductOptions", ExpQueryRegistryRoutine, 0)
.text:00000001405B04F8 mov edi, eax
.text:00000001405B04FA call ExInitLicenseData
查询注册表ProductOptions键值,ExpQueryRegistryRoutine为回调函数。
ExInitLicenseData初始化license的关键函数,过于复杂,未跟踪。
每个silo进程都有独立的注册表namespace。
PAGE:000000014068B2E0 CmpParseKey proc near
PAGE:000000014068B689 mov rcx, [r13+8]
PAGE:000000014068B68D call CmpGetRegistryNamespaceRootForSilo
CmpGetRegistryNamespaceRootForSilo调用PsGetPermanentSiloContext获取ejob->Storage的地址。
PAGE:0000000140687B1F mov rax, [rax+20h]
ejob->Storage.slots[32]为vreg namespace的地址。
PAGE:00000001406746A0 ; NTSTATUS __stdcall PsLookupThreadByThreadId(HANDLE ThreadId, PETHREAD *Thread)
PAGE:00000001406746A0 public PsLookupThreadByThreadId
PAGE:00000001406746A0 PsLookupThreadByThreadId proc near
PAGE:00000001406746B5 mov rsi, gs:188h ; CurrentThread
PAGE:00000001406746BE mov r14, rdx
PAGE:00000001406746C1 dec word ptr [rsi+1E6h] ; --CurrentThread->SpecialApcDisable
PAGE:00000001406746C8 mov dl, 6
PAGE:00000001406746CA call PspReferenceCidTableEntry ; (ThreadId, 6)
PspReferenceCidTableEntry函数根据ThreadId返回对应的ethread结构体。
PAGE:00000001406746D7 call PsGetCurrentServerSilo
得到当前进程的ejob结构体。
PAGE:000000014067470D loc_14067470D: ; CODE XREF: PsLookupThreadByThreadId+48↑j
PAGE:000000014067470D mov rcx, [rbx+220h] ; (kproces *)thread->tcb->process
PAGE:0000000140674714 mov rdx, rdi
PAGE:0000000140674717 call PsIsProcessInSilo ; (kprocess, ejob)
PAGE:000000014067471C test al, al
PAGE:000000014067471E jnz short loc_14067472A
PAGE:0000000140674720
PAGE:0000000140674720 loc_140674720: ; CODE XREF: PsLookupThreadByThreadId+6B↑j
PAGE:0000000140674720 mov rcx, rbx ; DmaAdapter
PAGE:0000000140674723 call HalPutDmaAdapter
PAGE:0000000140674728 xor ebx, ebx
PsIsProcessInSilo函数有两个参数,第一个参数为要得到handle的目标进程kprocess结构体, 第二个参数为当前进程的ejob结构体,如果返回值为0就不能获取目标进程的Handle,为1则可以。
下面仔细分析下这个函数:
.text:0000000140273948 PsIsProcessInSilo proc near ; CODE XREF: PsIsThreadInSilo+29↑p
.text:0000000140273948 ; PsLookupThreadByThreadId+77↓p ...
.text:0000000140273948 sub rsp, 28h
.text:000000014027394C test rdx, rdx
.text:000000014027394F jnz short loc_140273959
.text:0000000140273951
.text:0000000140273951 loc_140273951: ; CODE XREF: PsIsProcessInSilo+18↓j
.text:0000000140273951 ; PsIsProcessInSilo+21↓j
.text:0000000140273951 mov al, 1 ; ejob == NULL
.text:0000000140273953
.text:0000000140273953 loc_140273953: ; CODE XREF: PsIsProcessInSilo+3C↓j
.text:0000000140273953 add rsp, 28h
.text:0000000140273957 retn
如果第二个参数ejob为空则返回1。
.text:0000000140273959
.text:0000000140273959 loc_140273959: ; CODE XREF: PsIsProcessInSilo+7↑j
.text:0000000140273959 cmp rcx, cs:PsInitialSystemProcess
.text:0000000140273960 jz short loc_140273951 ; ejob == NULL
.text:0000000140273962 cmp rcx, cs:PsIdleProcess
.text:0000000140273969 jz short loc_140273951 ; ejob == NULL
如果第一个参数kprocess为PsInitialSystemProcess或PsIdleProcess同样返回为1。
.text:000000014027396B mov rcx, [rcx+510h] ; eprocess->job
.text:0000000140273972 call PspGetJobSilo
PspGetJobSilo函数从当前进程的ejob开始向父进程ejob查找到第一个包含silo状态的ejob结构体。
.text:0000000140273977 mov rcx, rax
.text:000000014027397A call PspIsSiloInSilo
.text:000000014027397F test al, al
.text:0000000140273981 setnz al
.text:0000000140273984 jmp short loc_140273953
.text:0000000140273984 PsIsProcessInSilo endp
PspIsSiloInSilo函数包含两个参数,第一个参数为目标进程包含silo状态的ejob结构体,第二个参数为当前进程的ejob结构体。如果返回值不为0,则返回1。
PAGE:000000014064BB6C PspIsSiloInSilo proc near ; CODE XREF: PsIsProcessInSilo+32↑p
PAGE:000000014064BB6C ; PsIsThreadInSilo+1EA483↑p ...
PAGE:000000014064BB6C test rdx, rdx
PAGE:000000014064BB6F jnz short loc_14064BB75
PAGE:000000014064BB71
PAGE:000000014064BB71 loc_14064BB71: ; CODE XREF: PspIsSiloInSilo+1BA6A7↓j
PAGE:000000014064BB71 mov al, 1
PAGE:000000014064BB73 retn
如果第二个参数ejob为0,则返回1。
PAGE:000000014064BB75 loc_14064BB75: ; CODE XREF: PspIsSiloInSilo+3↑j
PAGE:000000014064BB75 ; PspIsSiloInSilo+1BA6B4↓j
PAGE:000000014064BB75 test rcx, rcx
PAGE:000000014064BB78 jnz loc_140806210
PAGE:000000014064BB7E xor al, al
PAGE:000000014064BB80 retn
PAGE:0000000140806210 loc_140806210: ; CODE XREF: PspIsSiloInSilo+C↑j
PAGE:0000000140806210 cmp rcx, rdx
PAGE:0000000140806213 jz loc_14064BB71
PAGE:0000000140806219 mov rcx, [rcx+430h]
PAGE:0000000140806220 jmp loc_14064BB75
通过一个循环遍历第一个参数及它的父进程ejob是否和第二个参数相等,如果相等返回1,否则返回0。所以通过上述两个函数的分析,一个进程能否获取另一个进程的handle,有以下几种情况:
1、如果当前进程没有silo结构,也就是说当前进程在容器外,是可以得到容器内进程的handle。
2、PsInitialSystemProcess和PsIdleProcess进程能获取任何容器内进程的handle。
3、容器内的进程是不能获取容器外的任何进程handle。
4、只有父亲容器可以获取子容器内进程的handle,反之不可以。
silo进程内不能安装和卸载驱动程序。
.text:0000000140399388 ; __int64 __fastcall IopLoadDriverImage(_OWORD *)
.text:0000000140399388 IopLoadDriverImage proc near ; CODE XREF: NtLoadDriver+4↓p
.text:0000000140399388
.text:00000001403993EE call PsIsCurrentThreadInServerSilo
.text:00000001403993F3 test al, al
.text:00000001403993F5 jnz loc_140399568
如果进程在container内是不允许加载驱动程序的。
PAGE:0000000140762F24 IopUnloadDriver proc near ; CODE XREF: PnpUnloadAttachedDriver+A1↑p
PAGE:0000000140762F24
PAGE:0000000140762FA2 call PsIsCurrentThreadInServerSilo
PAGE:0000000140762FA7 test al, al
PAGE:0000000140762FA9 jnz loc_14081B8E9
同理,contianer进程不能卸载驱动程序。
AGE:000000014068F290 ObpLookupObjectName proc near ; CODE XREF: ObReferenceObjectByNameEx+156↑p
PAGE:000000014068F290
PAGE:000000014068F4C6 mov edx, cs:PsObjectDirectorySiloContextSlot
PAGE:000000014068F4CC mov [rbp+0C0h+var_D0], r8
PAGE:000000014068F4D0 lea r8, [rbp+0C0h+var_D0]
PAGE:000000014068F4D4 call PsGetPermanentSiloContext
PAGE:000000014068F4D9 test eax, eax
PAGE:000000014068F4DB jns loc_140835AE0
PAGE:000000014068F4E1 mov r14, cs:ObpRootDirectoryObject
如果在容器中则通过PsGetPermanentSiloContext获取root directory, 否则使用ObpRootDirectoryObject作为root directory。
.text:00000001402772A0 PsGetPermanentSiloContext proc near ; CODE XREF: CmGetRootKeyObjectForSilo+17↓p
.text:00000001402772A0 test rcx, rcx
.text:00000001402772A3 jnz short loc_1402772DB
.text:00000001402772A5 mov rcx, cs:qword_140D23990
.text:00000001402772AC
.text:00000001402772AC loc_1402772AC: ; CODE XREF: PsGetPermanentSiloContext+42↓j
.text:00000001402772AC mov qword ptr [r8], 0
.text:00000001402772B3 cmp edx, 20h ; ' '
.text:00000001402772B6 jnb loc_14041937E
.text:00000001402772BC
.text:00000001402772BC loc_1402772BC: ; CODE XREF: PsGetPermanentSiloContext+1A20F9↓j
.text:00000001402772BC mov eax, edx
.text:00000001402772BE add rax, rax
.text:00000001402772C1 mov rcx, [rcx+rax*8+8]
.text:00000001402772C6 mov rax, rcx
.text:00000001402772C9 and rax, 0FFFFFFFFFFFFFFFEh
.text:00000001402772CD jz short loc_1402772E4
.text:00000001402772CF test cl, 1
.text:00000001402772D2 jz short loc_1402772EB
.text:00000001402772D4 mov [r8], rax
.text:00000001402772D7 xor eax, eax
.text:00000001402772D9 retn
.text:00000001402772D9 ; ---------------------------------------------------------------------------
.text:00000001402772DA db 0CCh
.text:00000001402772DB ; ---------------------------------------------------------------------------
.text:00000001402772DB
.text:00000001402772DB loc_1402772DB: ; CODE XREF: PsGetPermanentSiloContext+3↑j
.text:00000001402772DB mov rcx, [rcx+518h]
.text:00000001402772E2 jmp short loc_1402772AC
ejob->Storage[PsObjectDirectorySiloContextSlot].value为当前容器的root directory。
OBP_GET_SILO_ROOT_DIRECTORY_FROM_SILO函数有同样的功能。