开篇福利
FkUpdate
Win10自动更新是真的烦人,每次按照网上的步骤禁用自动更新后,不用过多久系统又自动恢复了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:OpenSCManager
OpenService
StartService
ControlService
QueryServiceStatus
ChangeServiceConfig
CloseServiceHandle
注册表ACL相关
核心API:GetNamedSecurityInfo
SetNamedSecurityInfo
SetEntriesInAcl
GetExplicitEntriesFromAcl
AllocateAndInitializeSid
DeleteAce
具体思路禁用自动更新
:第一步停止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:GetFileAttributes
splitPath(自己封装路径分割)
ACL相关
核心API:GetNamedSecurityInfo
SetNamedSecurityInfo
SetEntriesInAcl
GetExplicitEntriesFromAcl
AllocateAndInitializeSid
DeleteAce
核心思路锁文件
:第一步获取上层目录的路径,第二步上层目录禁用继承,第三步上层目录设置拒绝删除子文件的属性,第四步当前文件禁用继承,第五步当前文件添加拒绝删除的属性,第六步更改文件所有者为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;
}