使用ERROR_ACCESS_DENIED删除每个API调用失败的文件夹

时间:2017-09-28 15:54:57

标签: c++ winapi windows-10 delete-file access-denied

我创建了一个CreateDirectoryW的文件夹,我无法访问该文件夹。我使用nullptr作为安全描述符,但由于某种原因,它没有复制父文件夹的ACL,而是使文件夹无法访问。

我无法查看或更改所有者。 takeownicaclsSetNamedSecurityInfoW,所有来自提升的流程或命令提示,都会失败并显示ERROR_ACCESS_DENIED

在尝试使用Linux live CD之前,我是否有机会在Windows(Shell或C ++)中删除此文件夹?希望这些CD不关心ACL?

2 个答案:

答案 0 :(得分:6)

您只需启用备份(或恢复)权限:

#include <Windows.h>

#include <stdio.h>

int wmain(int argc, wchar_t ** argv)
{
    // argv[1] must contain the directory to remove

    HANDLE hToken;
    struct 
    {    
        DWORD PrivilegeCount;
        LUID_AND_ATTRIBUTES Privileges[1];    
    } tkp;

    if (OpenProcessToken(GetCurrentProcess(), 
        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) 
    {
        LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &tkp.Privileges[0].Luid);

        tkp.PrivilegeCount = 1;
        tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

        if (!AdjustTokenPrivileges(hToken, FALSE, 
            (PTOKEN_PRIVILEGES)&tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0))
        {
            printf("AdjustTokenPrivileges: %u\n", GetLastError());
            return 1;
        }

        if (GetLastError() != ERROR_SUCCESS)
        {
            // This happens if you don't have the privilege
            printf("AdjustTokenPrivileges: %u\n", GetLastError());
            return 1;
        }

        CloseHandle(hToken);
    }

    if (!RemoveDirectory(argv[1]))
    {
        printf("RemoveDirectory: %u\n", GetLastError());
        return 1;
    }

    printf("OK\n");
    return 0;
}

请注意,为了简明起见,省略了一些错误处理。另请注意,AdjustTokenPrivileges()是少数特殊情况之一,即使调用成功,调用GetLastError()也是有意义的;它将返回ERROR_SUCCESSERROR_NOT_ALL_ASSIGNED,具体取决于您是否具有您尝试启用的所有权限。

这是绕过文件安全权限的相当普遍的解决方案。它适用于大多数API调用,但在某些情况下(最值得注意的是CreateFile),您必须提供特殊标志才能使用备份权限。除了删除文件或删除您没有权限的目录外,您还可以更改属性,更改权限,甚至将所有权分配给其他人,这是不允许的。

答案 1 :(得分:2)

假设文件未打开且与删除共享标志不兼容且文件中没有任何部分。

删除文件足够2件事 - 我们在文件夹上有FILE_DELETE_CHILD。和文件不是只读的。在这种情况下,请调用ZwDeleteFile(但不是DeleteFileRemoveDirectory - 如果文件空 DACL ),则此api将失败。如果文件具有只读属性 - ZwDeleteFile失败,代码为STATUS_CANNOT_DELETE。在这种情况下,我们首先需要删除只读。为此需要打开FILE_WRITE_ATTRIBUTES访问权限的文件。如果我们在调用ZwOpenFileSE_RESTORE_PRIVILEGE并设置FILE_OPEN_FOR_BACKUP_INTENT选项,我们就可以执行此操作。所以删除文件的代码可以是下一个:

NTSTATUS DeleteEx(POBJECT_ATTRIBUTES poa)
{
    NTSTATUS status = ZwDeleteFile(poa);

    if (status == STATUS_CANNOT_DELETE)
    {
        BOOLEAN b;
        RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &b);
        HANDLE hFile;

        IO_STATUS_BLOCK iosb;
        if (0 <= (status = NtOpenFile(&hFile, FILE_WRITE_ATTRIBUTES, poa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT)))
        {
            static FILE_BASIC_INFORMATION fbi = {{},{},{},{}, FILE_ATTRIBUTE_NORMAL};
            status = ZwSetInformationFile(hFile, &iosb, &fbi, sizeof(fbi), FileBasicInformation);
            NtClose(hFile);

            if (0 <= status)
            {
                status = ZwDeleteFile(poa);
            }
        }
    }

    return status;
}