教你利用Windows訪問控制搞事情

開篇福利

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 - 訪問令牌

詳細解釋
從簡單到複雜的依次解釋

  1. SID,用於標識用戶,組和計算機帳戶,首次創建帳戶時會獲得一個唯一的SID用於標識該賬戶。簡單來說就如同每個大學生入學都會分配一個唯一的學號,這個學號就是你的證明
  2. ACL,用來說明某個對象的訪問權限,由DACL和SACL組成,具體的某項權限稱爲ACE。簡單來說就如同大學校園裏的各項規定,任何一個對象都有它特定的規則,假設某個具體對象爲教室裏的電腦,學生們只能看,而老師們可以操控,這就是教室裏的電腦這個對象的訪問權限
  3. SD,包含與安全對象相關的一些安全信息,包括該對象的所有者和所屬組的SID,DACL,SACL以及一組控制位(用於限定所有者SID,DACL和SACL)
  4. 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;
}

END

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章