ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial, base::win::ScopedHandle* lockdown, base::win::ScopedHandle* lowbox){ // Create the 'naked' token. This will be the permanent token associated // with the process and therefore with any thread that is not impersonating. // 这是个很关键的API,在进程token基础上,做出一个受限信令,使用的是默认DACL // restricted token是Windows的一个重要概念 DWORD result = CreateRestrictedToken(lockdown_level_, integrity_level_, PRIMARY, lockdown_default_dacl_, lockdown); if (ERROR_SUCCESS != result) return SBOX_ERROR_GENERIC;
// If we're launching on the alternate desktop we need to make sure the // integrity label on the object is no higher than the sandboxed process's // integrity level. So, we lower the label on the desktop process if it's // not already low enough for our process. // 这一部分是对alternative desktop IL的调整,它不应该高于target的IL,所以如果 // IL不够低,就要削成一样低 if (use_alternate_desktop_ && integrity_level_ != INTEGRITY_LEVEL_LAST) { // Integrity label enum is reversed (higher level is a lower value). static_assert(INTEGRITY_LEVEL_SYSTEM < INTEGRITY_LEVEL_UNTRUSTED, "Integrity level ordering reversed."); HDESK desktop_handle = nullptr; IntegrityLevel desktop_integrity_level_label; if (use_alternate_winstation_) { desktop_handle = alternate_desktop_handle_; desktop_integrity_level_label = alternate_desktop_integrity_level_label_; } else { desktop_handle = alternate_desktop_local_winstation_handle_; desktop_integrity_level_label = alternate_desktop_local_winstation_integrity_level_label_; } // If the desktop_handle hasn't been created for any reason, skip this. if (desktop_handle && desktop_integrity_level_label < integrity_level_) { result = SetObjectIntegrityLabel(desktop_handle, SE_WINDOW_OBJECT, L"", GetIntegrityLevelString(integrity_level_)); if (ERROR_SUCCESS != result) return SBOX_ERROR_GENERIC;
```cpp ResultCode PolicyBase::AddTarget(TargetProcess* target){ if (policy_) policy_maker_->Done();
// mitigation实装 if (!ApplyProcessMitigationsToSuspendedProcess(target->Process(), mitigations_)) { return SBOX_ERROR_APPLY_ASLR_MITIGATIONS; }
// 对target设置所有的Interceptions,Interception是一种对target进程的Hook机制 // 内容非常丰富,以后会分析 ResultCode ret = SetupAllInterceptions(target);
if (ret != SBOX_ALL_OK) return ret;
// broker为target进程开辟一块空间,存储传过去的handles // 还要把g_handles_to_close值传过去 if (!SetupHandleCloser(target)) return SBOX_ERROR_SETUP_HANDLE_CLOSER;
DWORD win_error = ERROR_SUCCESS; // Initialize the sandbox infrastructure for the target. // TODO(wfh) do something with win_error code here. // 这里面别有洞天,现在知道传过去的都是什么鬼了 ret = target->Init(dispatcher_.get(), policy_, kIPCMemSize, kPolMemSize, &win_error);
// Construct the IPC server and the IPC dispatcher. When the target does // an IPC it will eventually call the dispatcher. ResultCode TargetProcess::Init(Dispatcher* ipc_dispatcher, void* policy, uint32_t shared_IPC_size, uint32_t shared_policy_size, DWORD* win_error){ // We need to map the shared memory on the target. This is necessary for // any IPC that needs to take place, even if the target has not yet hit // the main( ) function or even has initialized the CRT. So here we set // the handle to the shared section. The target on the first IPC must do // the rest, which boils down to calling MapViewofFile()
// We use this single memory pool for IPC and for policy. // 使用了shared_mem的IPC通信方式,以后会详细分析这些内容 DWORD shared_mem_size = static_cast<DWORD>(shared_IPC_size + shared_policy_size); shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE | SEC_COMMIT, 0, shared_mem_size, nullptr)); if (!shared_section_.IsValid()) { *win_error = ::GetLastError(); return SBOX_ERROR_CREATE_FILE_MAPPING; }
DWORD access = FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY; HANDLE target_shared_section; if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_.Get(), sandbox_process_info_.process_handle(), &target_sharedࠤ؍是很清楚它的作用 if (lowbox_sid_) { if (!lowbox_directory_.IsValid()) { result = CreateLowBoxObjectDirectory(lowbox_sid_, true, &lowbox_directory_); DCHECK(result == ERROR_SUCCESS); }
// The order of handles isn't important in the CreateLowBoxToken call. // The kernel will maintain a reference to the object directory handle. HANDLE saved_handles[1] = {lowbox_directory_.Get()}; DWORD saved_handles_count = lowbox_directory_.IsValid() ? 1 : 0;
// Create the 'better' token. We use this token as the one that the main // thread uses when booting up the process. It should contain most of // what we need (before reaching main( )) // initial token的创建使用的是和lockdown同样的接口,但它是IMPERSONATION而lockdown是PRIMARY result = CreateRestrictedToken(initial_level_, integrity_level_, IMPERSONATION, lockdown_default_dacl_, initial); if (ERROR_SUCCESS != result) return SBOX_ERROR_GENERIC;
// Handles the creation of a restricted token using the effective token or // any token handle. // 在一个有效的token句柄之上,创建出一个权限更为严格的token // 在Windows中这种token叫受限令牌 // 可能从特权集中删除一些特权;该令牌SID可以被标记成Deny-only;该令牌SID可以被标记为 // restricted // Sample usage: // RestrictedToken restricted_token; // DWORD err_code = restricted_token.Init(nullptr); // Use the current // // effective token // if (ERROR_SUCCESS != err_code) { // // handle error. // } // // restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID()); // base::win::ScopedHandle token_handle; // err_code = restricted_token.GetRestrictedToken(&token_handle); // if (ERROR_SUCCESS != err_code) { // // handle error. // } // [...] classRestrictedToken { public: // Init() has to be called before calling any other method in the class. RestrictedToken(); ~RestrictedToken();
// Initializes the RestrictedToken object with effective_token. // If effective_token is nullptr, it initializes the RestrictedToken object // with the effective token of the current process. // 构造器接Init素质二连,如果effective_token是nullptr,就使用当前进程的token DWORD Init(HANDLE effective_token);
// Creates a restricted token. // If the function succeeds, the return value is ERROR_SUCCESS. If the // function fails, the return value is the win32 error code corresponding to // the error. // 这个就是在应用了所有的规则之后,创建出来的restricted token,在最后一步使用 // token显然是个OUT型参数 DWORD GetRestrictedToken(base::win::ScopedHandle* token)const;
// Creates a restricted token and uses this new token to create a new token // for impersonation. Returns this impersonation token. // // If the function succeeds, the return value is ERROR_SUCCESS. If the // function fails, the return value is the win32 error code corresponding to // the error. // // The sample usage is the same as the GetRestrictedToken function. // 与上面类似,只是生成的是个impersonation token DWORD GetRestrictedTokenForImpersonation( base::win::ScopedHandle* token)const;
// Lists all sids in the token and mark them as Deny Only except for those // present in the exceptions parameter. If there is no exception needed, // the caller can pass an empty list or nullptr for the exceptions // parameter. // 除了exceptions参数中指定的白名单Sid列表,token中的所有sid都被设置为Deny Only // // If the function succeeds, the return value is ERROR_SUCCESS. If the // function fails, the return value is the win32 error code corresponding to // the error. // // Sample usage: // std::vector<Sid> sid_exceptions; // sid_exceptions.push_back(ATL::Sids::Users().GetPSID()); // sid_exceptions.push_back(ATL::Sids::World().GetPSID()); // restricted_token.AddAllSidsForDenyOnly(&sid_exceptions); // Note: A Sid marked for Deny Only in a token cannot be used to grant // access to any resource. It can only be used to deny access. DWORD AddAllSidsForDenyOnly(std::vector<Sid>* exceptions);
// Adds a user or group SID for Deny Only in the restricted token. // Parameter: sid is the SID to add in the Deny Only list. // The return value is always ERROR_SUCCESS. // // Sample Usage: // restricted_token.AddSidForDenyOnly(ATL::Sids::Admins().GetPSID()); // 某个SID设为Deny Only DWORD AddSidForDenyOnly(const Sid& sid);
// Adds the user sid of the token for Deny Only in the restricted token. // If the function succeeds, the return value is ERROR_SUCCESS. If the // function fails, the return value is the win32 error code corresponding to // the error. // 这个是user SID的版本 DWORD AddUserSidForDenyOnly();
// Lists all privileges in the token and add them to the list of privileges // to remove except for those present in the exceptions parameter. If // there is no exception needed, the caller can pass an empty list or nullptr // for the exceptions parameter. // // If the function succeeds, the return value is ERROR_SUCCESS. If the // function fails, the return value is the win32 error code corresponding to // the error. // // Sample usage: // std::vector<base::string16> privilege_exceptions; // privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); // restricted_token.DeleteAllPrivileges(&privilege_exceptions); // 删除所有特权,除了白名单的那些privilege DWORD DeleteAllPrivileges(const std::vector<base::string16>* exceptions);
// Adds a privilege to the list of privileges to remove in the restricted // token. // Parameter: privilege is the privilege name to remove. This is the string // representing the privilege. (e.g. "SeChangeNotifyPrivilege"). // If the function succeeds, the return value is ERROR_SUCCESS. If the // function fails, the return value is the win32 error code corresponding to // the error. // // Sample usage: // restricted_token.DeletePrivilege(SE_LOAD_DRIVER_NAME); // 删除某个特权 DWORD DeletePrivilege(constwchar_t* privilege);
// Adds a SID to the list of restricting sids in the restricted token. // Parameter: sid is the sid to add to the list restricting sids. // The return value is always ERROR_SUCCESS. // // Sample usage: // restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID()); // Note: The list of restricting is used to force Windows to perform all // access checks twice. The first time using your user SID and your groups, // and the second time using your list of restricting sids. The access has // to be granted in both places to get access to the resource requested. // Restricting SID强制windows对所有检查进行两次。 // 第一次使用你的用户和组SID,第二次使用restricting SID列表的SID。 // 两次必须都被授予可访问,才能通过请求 DWORD AddRestrictingSid(const Sid& sid);
// Adds the logon sid of the token in the list of restricting sids for the // restricted token. // // If the function succeeds, the return value is ERROR_SUCCESS. If the // function fails, the return value is the win32 error code corresponding to // the error. // Logon SID是个特殊的SID,是登录会话进行时系统随机生成的(SID最后一节) DWORD AddRestrictingSidLogonSession();
// Adds the owner sid of the token in the list of restricting sids for the // restricted token. // // If the function succeeds, the return value is ERROR_SUCCESS. If the // function fails, the return value is the win32 error code corresponding to // the error. // 把所有者SID也加到restricting SID列表 DWORD AddRestrictingSidCurrentUser();
// Adds all group sids and the user sid to the restricting sids list. // // If the function succeeds, the return value is ERROR_SUCCESS. If the // function fails, the return value is the win32 error code corresponding to // the error. // token的所有用户/组 SID都加入到restricting sid列表 DWORD AddRestrictingSidAllSids();
// Sets the token integrity level. This is only valid on Vista. The integrity // level cannot be higher than your current integrity level. // IL是用于隔离同一所有者不同权限资源的机制,实际上windows上最终也会折射成一个SID // 只是SID的不同节值表示不同含义,并以此区分开 DWORD SetIntegrityLevel(IntegrityLevel integrity_level);
// Set a flag which indicates the created token should have a locked down // default DACL when created. voidSetLockdownDefaultDacl();
private: // The list of restricting sids in the restricted token. std::vector<Sid> sids_to_restrict_; // The list of privileges to remove in the restricted token. std::vector<LUID> privileges_to_disable_; // The list of sids to mark as Deny Only in the restricted token. std::vector<Sid> sids_for_deny_only_; // The token to restrict. Can only be set in a constructor. base::win::ScopedHandle effective_token_; // The token integrity level. Only valid on Vista. IntegrityLevel integrity_level_; // Tells if the object is initialized or not (if Init() has been called) bool init_; // Lockdown the default DACL when creating new tokens. bool lockdown_default_dacl_;
DWORD RestrictedToken::Init(const HANDLE effective_token){ if (init_) return ERROR_ALREADY_INITIALIZED;
HANDLE temp_token; if (effective_token) { // We duplicate the handle to be able to use it even if the original handle // is closed. // 使用受限令牌的WinAPI定式,复制句柄->打开进程令牌 if (!::DuplicateHandle(::GetCurrentProcess(), effective_token, ::GetCurrentProcess(), &temp_token, 0, false, DUPLICATE_SAME_ACCESS)) { return ::GetLastError(); } } else { if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &temp_token)) { return ::GetLastError(); } } // 到这里,effective_token_成员就是当前或某个进程的句柄了(取决于传入参数是不是空) effective_token_.Set(temp_token);
// Build the list of restricting sids from all groups. for (unsignedint i = 0; i < token_groups->GroupCount; ++i) { if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0) AddRestrictingSid(reinterpret_cast<SID*>(token_groups->Groups[i].Sid)); }
// SID_AND_ATTRIBUTES和LUID_AND_ATTRIBUTES是token存SID和privilege的结构体 SID_AND_ATTRIBUTES* deny_only_array = nullptr; if (deny_size) { deny_only_array = new SID_AND_ATTRIBUTES[deny_size];
for (unsignedint i = 0; i < sids_for_deny_only_.size(); ++i) { deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY;//这个是key deny_only_array[i].Sid = sids_for_deny_only_[i].GetPSID(); } }
SID_AND_ATTRIBUTES* sids_to_restrict_array = nullptr; if (restrict_size) { sids_to_restrict_array = new SID_AND_ATTRIBUTES[restrict_size];
for (unsignedint i = 0; i < restrict_size; ++i) { sids_to_restrict_array[i].Attributes = 0;//同理 sids_to_restrict_array[i].Sid = sids_to_restrict_[i].GetPSID(); } }
// 特权的结构和SID不一样 LUID_AND_ATTRIBUTES* privileges_to_disable_array = nullptr; if (privileges_size) { privileges_to_disable_array = new LUID_AND_ATTRIBUTES[privileges_size];
for (unsignedint i = 0; i < privileges_size; ++i) { privileges_to_disable_array[i].Attributes = 0; privileges_to_disable_array[i].Luid = privileges_to_disable_[i]; } }
bool result = true; HANDLE new_token_handle = nullptr; // The SANDBOX_INERT flag did nothing in XP and it was just a way to tell // if a token has ben restricted given the limiations of IsTokenRestricted() // but it appears that in Windows 7 it hints the AppLocker subsystem to // leave us alone. // Windows API真身在此 if (deny_size || restrict_size || privileges_size) { result = ::CreateRestrictedToken( effective_token_.Get(), SANDBOX_INERT, static_cast<DWORD>(deny_size), deny_only_array, static_cast<DWORD>(privileges_size), privileges_to_disable_array, static_cast<DWORD>(restrict_size), sids_to_restrict_array, &new_token_handle); } else { // Duplicate the token even if it's not modified at this point // because any subsequent changes to this token would also affect the // current process. result = ::DuplicateTokenEx(effective_token_.Get(), TOKEN_ALL_ACCESS, nullptr, SecurityIdentification, TokenPrimary, &new_token_handle); } auto last_error = ::GetLastError();
// 清理资源 if (deny_only_array) delete[] deny_only_array;
if (sids_to_restrict_array) delete[] sids_to_restrict_array;
if (privileges_to_disable_array) delete[] privileges_to_disable_array;
if (lockdown_default_dacl_) { // Don't add Restricted sid and also remove logon sid access. if (!RevokeLogonSidFromDefaultDacl(new_token.Get())) return ::GetLastError(); } else { // Modify the default dacl on the token to contain Restricted. if (!AddSidToDefaultDacl(new_token.Get(), WinRestrictedCodeSid, GRANT_ACCESS, GENERIC_ALL)) { return ::GetLastError(); } }
// Add user to default dacl. if (!AddUserSidToDefaultDacl(new_token.Get(), GENERIC_ALL)) return ::GetLastError();
// Creates a restricted token based on the effective token of the current // process. The parameter security_level determines how much the token is // restricted. The token_type determines if the token will be used as a primary // token or impersonation token. The integrity level of the token is set to // |integrity level| on Vista only. // |token| is the output value containing the handle of the newly created // restricted token. // |lockdown_default_dacl| indicates the token's default DACL should be locked // down to restrict what other process can open kernel resources created while // running under the token. // If the function succeeds, the return value is ERROR_SUCCESS. If the // function fails, the return value is the win32 error code corresponding to // the error. // 根据注释多少能明白一些,传入的这些参数都是PolicyBase对象在调用前就设置好了的成员变量 DWORD CreateRestrictedToken(TokenLevel security_level, IntegrityLevel integrity_level, TokenType token_type, bool lockdown_default_dacl, base::win::ScopedHandle* token){ RestrictedToken restricted_token; // 构造接Init素质二连 restricted_token.Init(nullptr); // Initialized with the current process token // 如果有默认DACL,就设置下去 if (lockdown_default_dacl) restricted_token.SetLockdownDefaultDacl();
// This token has to be able to create objects in BNO. // Unfortunately, on Vista+, it needs the current logon sid // in the token to achieve this. You should also set the process to be // low integrity level so it can't access object created by other // processes. restricted_token.AddRestrictingSidLogonSession(); break; } case USER_RESTRICTED: { privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); restricted_token.AddUserSidForDenyOnly(); restricted_token.AddRestrictingSid(WinRestrictedCodeSid); break; } case USER_LOCKDOWN: { restricted_token.AddUserSidForDenyOnly(); restricted_token.AddRestrictingSid(WinNullSid); break; } default: { return ERROR_BAD_ARGUMENTS; } }
DWORD err_code = ERROR_SUCCESS; if (deny_sids) { err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions); if (ERROR_SUCCESS != err_code) return err_code; }
if (remove_privileges) { err_code = restricted_token.DeleteAllPrivileges(&privilege_exceptions); if (ERROR_SUCCESS != err_code) return err_code; }
// Change the token of the main thread of the new process for the // impersonation token with more rights. This allows the target to start; // otherwise it will crash too early for us to help. // 主线程临时使用这个impersonation token HANDLE temp_thread = process_info.thread_handle(); if (!::SetThreadToken(&temp_thread, impersonation_token)) { *win_error = ::GetLastError(); ::TerminateProcess(process_info.process_handle(), 0); return SBOX_ERROR_SET_THREAD_TOKEN; } // 只能用一次,这个东西此后就没用了,关闭掉 initial_token_.Close(); }
ResultCode ret; // Set the global variables in the target. These are
not used on the broker. g_shared_section = target_shared_section; ret =
TransferVariable("g_shared_section", &g_shared_section,
sizeof(g_shared_section)); g_shared_section = nullptr; if (SBOX_ALL_OK
!= ret) { win_error = ::GetLastError(); return ret; }
g_shared_IPC_size = shared_IPC_size; ret =
TransferVariable("g_shared_IPC_size", &g_shared_IPC_size,
sizeof(g_shared_IPC_size)); g_shared_IPC_size = 0; if (SBOX_ALL_OK !=
ret) { win_error = ::GetLastError(); return ret; }
g_shared_policy_size = shared_policy_size; ret =
TransferVariable("g_shared_policy_size", &g_shared_policy_size,
sizeof(g_shared_policy_size)); g_shared_policy_size = 0; if (SBOX_ALL_OK
!= ret) { *win_error = ::GetLastError(); return ret; }
```cpp // Failure here is a breach of security so the process is terminated. void TargetServicesBase::LowerToken() { if (ERROR_SUCCESS != SetProcessIntegrityLevel(g_shared_delayed_integrity_level)) ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_INTEGRITY); process_state_.SetRevertedToSelf();
// If the client code as called RegOpenKey, advapi32.dll has cached some // handles. The following code gets rid of them. // 这个API名称虽然不起眼,但却大有作为,实际上他完成了从撤销impersonate到恢复primary token的过程 if (!::RevertToSelf()) ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_DROPTOKEN); // 这些先不关心,处理cache handle if (!FlushCachedRegHandles()) ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_FLUSHANDLES); if (ERROR_SUCCESS != ::RegDisablePredefinedCache()) ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE); if (!WarmupWindowsLocales()) ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_WARMUP); bool is_csrss_connected = true; if (!CloseOpenHandles(&is_csrss_connected)) ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES); process_state_.SetCsrssConnected(is_csrss_connected); // 处理mitigation // Enabling mitigations must happen last otherwise handle closing breaks if (g_shared_delayed_mitigations && !ApplyProcessMitigationsToCurrentProcess(g_shared_delayed_mitigations)) ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_MITIGATION); }
// 这个就一目了然了,使用新的integrity_level DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) { // We don't check for an invalid level here because we'll just let it // fail on the SetTokenIntegrityLevel call later on. if (integrity_level == INTEGRITY_LEVEL_LAST) { // No mandatory level specified, we don't change it. return ERROR_SUCCESS; }
HANDLE token_handle; if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT, &token_handle)) return ::GetLastError();
// Launches the app in the sandbox and ask it to wait in an // infinite loop. Waits for 2 seconds and then check if the // desktop associated with the app thread is not the same as the // current desktop. TEST(PolicyTargetTest, DesktopPolicy) { BrokerServices* broker = GetBroker();