教你利用Windows访问控制搞事情

6次阅读

共计 7063 个字符,预计需要花费 18 分钟才能阅读完成。

开篇福利
FkUpdateWin10 自动更新是真的烦人,每次按照网上的步骤禁用自动更新后,不用过多久系统又自动恢复了 Update!于是自己研究了访问控制,利用访问控制原理修改服务对应的注册表权限,让系统无法修改服务的状态,达到永久禁用自动更新的效果!目前为止尚未发现 Bug,所以共享给大家使用!只希望大家给文章点点赞!永久禁用 Windows 自动更新 – 下载连接
FileLocker 利用访问控制原理修改文件和上层目录的权限,使得文件不可被删除。目前为止也只能防止文件误删!之后可能会添加防止移动、防止修改等功能!防止文件误删 – 下载链接
概念普及
常用术语 ACL(Access Control List) – Windows 访问控制列表

DACL(Discretionary Access Control List) – 任意访问控制列表
SACL(System Access Control List) – 系统访问控制列表

ACE(Access Control Entries) – 访问控制条目
SD(Security Descriptor) – 安全描述符
SID(Security Identifier) – 安全标识符
AccessToken – 访问令牌
详细解释从简单到复杂的依次解释

SID,用于标识用户,组和计算机帐户,首次创建帐户时会获得一个唯一的 SID 用于标识该账户。简单来说就如同每个大学生入学都会分配一个唯一的学号,这个学号就是你的证明
ACL,用来说明某个对象的访问权限,由 DACL 和 SACL 组成,具体的某项权限称为 ACE。简单来说就如同大学校园里的各项规定,任何一个对象都有它特定的规则,假设某个具体对象为教室里的电脑,学生们只能看,而老师们可以操控,这就是教室里的电脑这个对象的访问权限
SD,包含与安全对象相关的一些安全信息,包括该对象的所有者和所属组的 SID,DACL,SACL 以及一组控制位(用于限定所有者 SID,DACL 和 SACL)
AccessToken,用来控制对安全对象的访问,访问令牌包含登录会话的安全信息,主要用来标识用户,用户组的权限。系统在用户登录时创建访问令牌,并且该用户执行的每个进程都具有该令牌的副本

注意 1:SACL 主要用于审核和记录访问的,DACL 才是具体的权限列表,所以我们平时讲的 ACL 通常是指 DACL 注意 2:SD 是为了方便编程提出的概念(一个结构体而已),实际上操作系统是利用 AccessToken 和 ACL 来确定对某个文件、进程等的访问权限
权限检查过程每个计算机账户在登录是都会获得一个访问令牌 AccessToken,这个令牌会说明当前用户的权限!而每个文件或其它对象都有它自己的访问控制列表 ACL,即说明哪些账户拥有哪些权限!当该账户尝试读取或改写某个文件时,操作系统会将当前账户的访问令牌权限和目标文件每个具体的权限 (ACE) 按顺序作比较(只与 SID 相同的 ACE 进行比较),直到发生以下事件:一、拒绝访问的 ACE 明确拒绝对线程访问令牌中列出的其中一个受托者请求的任何访问权限二、线程访问令牌中列出的受托者的一个或多个允许访问的 ACE 明确授予所有请求的访问权限三、已检查所有 ACE,并且仍然至少有一个未明确允许的请求访问权限,在这种情况下,隐式拒绝访问
注意:访问控制列表 ACL 中的 ACE 有几大原则(拒绝大于允许、权限最小化、权限继承性以及权限累加)
动手实践
查看文件 ACL 讲了半天 ACL 是不是感觉太抽象了,来实际看看什么是 ACL 吧!你只需要在任意文件上右键 - 属性 - 安全 - 高级就能看到该文件的 ACL 了!其中权限选项卡中就是 DACL,审核选项卡中就是 SACL,所有者拥有对 DACL 的完全控制权
其中 SYSTEM 用户组拥有对该文件的读取权限,这样一条具体的某个用户的某项权限就是 ACL 中的访问控制条目(ACE)
简单修改权限修改当前用户组对某个文件只有读取权限,假如当前用户组是管理员的话还需要修改 Administrators 组的权限!第一步右键 - 属性 - 安全并选中当前用户点击编辑
第二步只勾选允许 - 读取,发现不可勾选
第三步禁用继承,必须要禁用继承否则不可更改,点击高级 - 更改权限并取消勾选包括可从该对象的父项继承的权限,弹窗选择添加,然后确定
第四步重复第二步,并更改 Administrators 组的权限
第五步尝试改写该文件,会提示没有权限
注意:文件夹拥有继承和传播属性,文件拥有继承属性,继承属性很好理解就是直接复制父目录的 ACL,传播就是当前文件夹是否允许子文件或子文件夹继承
FkUpdate 核心讲解
服务相关开启关闭服务,必须用到的几个 API:OpenSCManagerOpenServiceStartServiceControlServiceQueryServiceStatusChangeServiceConfigCloseServiceHandle
注册表 ACL 相关核心 API:GetNamedSecurityInfoSetNamedSecurityInfoSetEntriesInAclGetExplicitEntriesFromAclAllocateAndInitializeSidDeleteAce
具体思路禁用自动更新:第一步停止 Update 服务,第二步 Update 启动状态改为禁用,第三步 Update 注册表所有者改为当前用户,第四步禁用继承并添加,第五步修改所有用户组的权限为只读,第六步注册表所有者改为 SYSTEM 恢复自动更新:第一步 Update 注册表所有者改为当前用户,第二步删除所有 ACL,第三步启用继承,第四步注册表所有者改为 SYSTEM,第五步 Update 服务状态改为自动,第六步启动 Update 服务
BOOL enableUpdate() {
changeObjectOwner(updateReg, FALSE);
enInherit(updateReg);
changeObjectOwner(updateReg, TRUE);
changeStartType(updateServ, SERVICE_AUTO_START);
startSrv(updateServ);
return TRUE;
}

BOOL disableUpdate() {
PACL pOldDACL = NULL, pNewDACL = NULL;
DWORD dwRes = 0, dwSize = 0, i = 0;
PSECURITY_DESCRIPTOR pSD;
SID_NAME_USE eUse = SidTypeUnknown;
PEXPLICIT_ACCESS pEa;
ULONG uCount;

stopSrv(updateServ);
changeStartType(updateServ, SERVICE_DISABLED);
changeObjectOwner(updateReg, FALSE);
// disInheritDelete(updateReg);
enInherit(updateReg);
disInheritCopy(updateReg);
dwRes = GetNamedSecurityInfo(updateReg, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, NULL, NULL, &pOldDACL, NULL, &pSD);
if (dwRes != ERROR_SUCCESS) {
printf(“GetNamedSecurityInfo Error %u\n”, dwRes);
return FALSE;
}
if (ERROR_SUCCESS == GetExplicitEntriesFromAcl(pOldDACL, &uCount, &pEa)) {
for (i = 0; i < uCount; i++) {
pEa[i].grfAccessPermissions = GENERIC_READ;
}
}
if (ERROR_SUCCESS != SetEntriesInAcl(uCount, pEa, NULL, &pNewDACL)) {
printf(“Failed SetEntriesInAcl\n”);
return FALSE;
}
dwRes = SetNamedSecurityInfo(updateReg, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, NULL, NULL, pNewDACL, NULL);
if (ERROR_SUCCESS == dwRes) {
printf(“Successfully Changed DACL\n”);
}
changeObjectOwner(updateReg, TRUE);

if (pOldDACL)
LocalFree(pOldDACL);
if (pNewDACL)
LocalFree(pNewDACL);
if (pSD)
LocalFree(pSD);
if(pEa)
LocalFree(pEa);
return TRUE;
}
FileLocker 核心讲解
文件相关核心 API:GetFileAttributessplitPath(自己封装路径分割)
ACL 相关核心 API:GetNamedSecurityInfoSetNamedSecurityInfoSetEntriesInAclGetExplicitEntriesFromAclAllocateAndInitializeSidDeleteAce
核心思路锁文件:第一步获取上层目录的路径,第二步上层目录禁用继承,第三步上层目录设置拒绝删除子文件的属性,第四步当前文件禁用继承,第五步当前文件添加拒绝删除的属性,第六步更改文件所有者为 SYSTEM 恢复文件:第一步获取上层目录的路径,第二步上层目录启用继承并删除之前的 ACL,第三步当前文件启用继承并删除之前的 ACL,第四步更改文件的所有者为 SYSTEM
BOOL lockFile(LPTSTR lpMyFile) {
DWORD dwRes, i;
PACL pOldDACL = NULL, pNewDACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
PEXPLICIT_ACCESS pEa;
ULONG uCount;
CHAR lpFileName[MAX_PATH] = {0};

/* 首先得到上层目录路径 */
splitPath(lpMyFile, lpFileName);

/* 上层目录拥有者改为 Admin */
changeObjectOwner(lpFileName, FALSE);

/* 禁止上层目录继承 */
disInheritCopy(lpFileName);

/* 保存一份原始 DACL */
dwRes = GetNamedSecurityInfo(lpFileName, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pOldDACL, NULL, &pSD);
if (dwRes != ERROR_SUCCESS) {
printf(“GetNamedSecurityInfo Error %u\n”, dwRes);
return FALSE;
}

/* 设置拒绝删除子文件夹的属性 */
if (ERROR_SUCCESS == GetExplicitEntriesFromAcl(pOldDACL, &uCount, &pEa)) {
for (i = 0; i < uCount; i++) {
/*
public enum ACCESS_MASK {
READ_FILE = 0x000001,
WRITE_FILE = 0x000002,
CREATE_SUBDIR = 0x000004,
READ_EXT_ATTR = 0x000008,
WRITE_EXT_ATTR = 0x000010,
EXECUTE = 0x000020,
DELETE_DIR = 0x000040,
READ_FILE_ATTR = 0x000080,
WRITE_FILE_ATTR = 0x000100,
DELETE = 0x010000,
READ_SD = 0x020000,
WRITE_DACL = 0x040000,
WRITE_OWNER = 0x080000,
SYNCHRONIZE = 0x100000,
SHARE_READ = READ_FILE | READ_EXT_ATTR | EXECUTE | READ_FILE_ATTR | READ_SD | SYNCHRONIZE,
SHARE_CHANGE = SHARE_READ | WRITE_FILE | CREATE_SUBDIR | WRITE_EXT_ATTR | WRITE_FILE_ATTR | DELETE,
SHARE_FULL = SHARE_CHANGE | DELETE_DIR | WRITE_DACL | WRITE_OWNER
}
*/
pEa[i].grfAccessPermissions = 0x40;
pEa[i].grfAccessMode = DENY_ACCESS;
pEa[i].grfInheritance = NO_INHERITANCE;
}
}
if (ERROR_SUCCESS != SetEntriesInAcl(uCount, pEa, pOldDACL, &pNewDACL)) {
printf(“Failed SetEntriesInAcl\n”);
return FALSE;
}

/* 设置新的 DACL */
dwRes = SetNamedSecurityInfo(lpFileName, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, pNewDACL, NULL);
if (dwRes != ERROR_SUCCESS) {
printf(“SetNamedSecurityInfo Error %u\n”, dwRes);
return FALSE;
}

/* 上层目录拥有者改回 System */
changeObjectOwner(lpFileName, TRUE);

/* 当前文件或目录的拥有者改为 Admin */
changeObjectOwner(lpMyFile, FALSE);

/* 当前文件或目录禁止继承 */
disInheritCopy(lpMyFile);

/* 保留当前文件或目录的 DACL */
dwRes = GetNamedSecurityInfo(lpMyFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pOldDACL, NULL, &pSD);
if (dwRes != ERROR_SUCCESS) {
printf(“GetNamedSecurityInfo Error %u\n”, dwRes);
return FALSE;
}

/* 设置拒绝属性 */
if (ERROR_SUCCESS == GetExplicitEntriesFromAcl(pOldDACL, &uCount, &pEa)) {
for (i = 0; i < uCount; i++) {
pEa[i].grfAccessPermissions = DELETE;
pEa[i].grfAccessMode = DENY_ACCESS;
pEa[i].grfInheritance = NO_INHERITANCE;
}
}
if (ERROR_SUCCESS != SetEntriesInAcl(uCount, pEa, pOldDACL, &pNewDACL)) {
printf(“Failed SetEntriesInAcl\n”);
return FALSE;
}

/* 设置新的 DACL */
dwRes = SetNamedSecurityInfo(lpMyFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, pNewDACL, NULL);
if (dwRes != ERROR_SUCCESS) {
printf(“SetNamedSecurityInfo Error %u\n”, dwRes);
return FALSE;
}
changeObjectOwner(lpMyFile, TRUE);

/*
SECURITY_DESCRIPTOR SD;
InitializeSecurityDescriptor(&SD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&SD, TRUE, NULL, FALSE);
*/
if (pOldDACL)
LocalFree(pOldDACL);
if (pNewDACL)
LocalFree(pNewDACL);
if (pSD)
LocalFree(pSD);
if (pEa)
LocalFree(pEa);

return TRUE;
}

BOOL recoveryFile(LPTSTR lpMyFile) {
CHAR lpFileName[MAX_PATH] = {0};

/* 首先得到上层目录路径 */
splitPath(lpMyFile, lpFileName);

changeObjectOwner(lpFileName, FALSE);
enInherit(lpFileName);
changeObjectOwner(lpFileName, TRUE);

changeObjectOwner(lpMyFile, FALSE);
enInherit(lpMyFile);
changeObjectOwner(lpMyFile, TRUE);

return TRUE;
}
END

正文完
 0