【问题标题】:Remove file locked by another process删除被另一个进程锁定的文件
【发布时间】:2014-10-21 08:58:33
【问题描述】:

在测试应用程序稳定性时,我试图删除/损坏应用程序使用的数据库。操作系统是 Windows,文件系统是 NTFS,测试语言是 C++。

我需要将任何内容粘贴到数据库文件中或将其删除。但它在运行期间被测试应用程序锁定。所以也许有人以前遇到过这种情况,你可以给我/我们一个关于绕过测试代码锁定的可能方法的例子?理论或来源 - 感谢您提供任何帮助。

【问题讨论】:

  • 我们在谈论什么数据库? (顺便说一句:考虑使用 windowstesting 重新标记您的问题,以解决更具体的受众)
  • 那是 sqlite。但我认为即使那是一个文本文件也没关系。我所需要的只是以某种方式绕过锁定并通过附加随机字节或删除这些字节来破坏使用的文件。

标签: c++ windows file-locking


【解决方案1】:
#include <Windows.h>
#include <stdio.h>
#include <string.h>
#include <tchar.h>
#include <iostream>
#include <FileAPI.h>
#include <WinBase.h>
#include <conio.h>
#include <ctype.h>

#define START_ALLOC                 0x1000
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
#define SystemHandleInformation     0x10

typedef long(__stdcall *NtQSI)(
    ULONG  SystemInformationClass,
    PVOID  SystemInformation,
    ULONG  SystemInformationLength,
    PULONG ReturnLength
    );

typedef struct _SYSTEM_HANDLE_ENTRY {
    ULONG  OwnerPid;
    BYTE   ObjectType;
    BYTE   HandleFlags;
    USHORT HandleValue;
    PVOID  ObjectPointer;
    ACCESS_MASK  AccessMask;
} SYSTEM_HANDLE_ENTRY, *PSYSTEM_HANDLE_ENTRY;

int main(int argc, char *argv[])
{
    HMODULE hNtDll = NULL;
    NtQSI   pNtQSI = NULL;
    PVOID   pMem = NULL;
    ULONG   allocSize = START_ALLOC;
    ULONG   retVal = 0;
    // --------------------------------
    ULONG   hCount = 0;
    PSYSTEM_HANDLE_ENTRY hFirstEntry = NULL;
    // --------------------------------
    ULONG   i;

    hNtDll = LoadLibraryA("NTDLL.dll");

    if (!hNtDll)
        return 1;

    pNtQSI = (NtQSI)GetProcAddress(hNtDll, "NtQuerySystemInformation");

    if (!pNtQSI) {
        FreeLibrary(hNtDll);
        return 2;
    }

    pMem = malloc(allocSize);

    while (pNtQSI(SystemHandleInformation, pMem, allocSize, &retVal)
        == STATUS_INFO_LENGTH_MISMATCH) {
        pMem = realloc(pMem, allocSize *= 2);
    }

    hCount = *(ULONG*)pMem;
    hFirstEntry = (PSYSTEM_HANDLE_ENTRY)((PBYTE)pMem + 4);

    int pid = atoi(argv[1]);

    for (i = 0; i < hCount; ++i)
    if ((hFirstEntry[i].ObjectType == 30) && (hFirstEntry[i].OwnerPid == pid))
    {
        HANDLE TargetHandleValueTemp = (HANDLE)hFirstEntry[i].HandleValue;
        HANDLE SourceProcHandleTemp = OpenProcess(PROCESS_DUP_HANDLE, FALSE, hFirstEntry[i].OwnerPid);
        char confirm ='n';

        DuplicateHandle(SourceProcHandleTemp, (HANDLE)hFirstEntry[i].HandleValue, GetCurrentProcess(), &TargetHandleValueTemp, 0, FALSE, DUPLICATE_SAME_ACCESS);

        TCHAR Path[MAX_PATH];
        DWORD dwret = GetFinalPathNameByHandle(TargetHandleValueTemp, Path, MAX_PATH, 0);
        if (!argv[2])
        {
            _tprintf(TEXT("PID: %d\tFileHandle: %d\tThe final path is: %s\n"), hFirstEntry[i].HandleValue, TargetHandleValueTemp, Path);
        }
        else if (_tcsstr(Path, _T(argv[2])))
        {
            _tprintf(TEXT("PID: %d\tFileHandle: %d\tThe final path is: %s\n\t Remove it? (y/n): "), hFirstEntry[i].HandleValue, TargetHandleValueTemp, Path);
            _flushall();
            std::cin.get(confirm);
            if (confirm == 'y')
                DuplicateHandle(SourceProcHandleTemp, (HANDLE)hFirstEntry[i].HandleValue, GetCurrentProcess(), &TargetHandleValueTemp, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
        }

        CloseHandle(SourceProcHandleTemp);
        CloseHandle(TargetHandleValueTemp);
    }

    free(pMem);
    FreeLibrary(hNtDll);
}

【讨论】:

  • 这个适用于 Windows 8.1。如果您有其他操作系统版本,请尝试使用 hFirstEntry[i].ObjectType == 30 。以前的版本应该是 28
  • 你怎么称呼它? (一个示例电话会很棒)
  • program.exe 但这段代码当时适用于 Windows 8.1。在 10 中,我很确定您需要尝试使用 hFirstEntry[i].ObjectType == 30 并将 30 更改为其他值(尝试 29、31、32)。它是(或者实际上当时是)Windows 的无证 API,很难找到这个描述
【解决方案2】:

您必须关闭应用程序对文件的句柄。请记住,我相信您必须在应用程序中再次打开文件。

我发现了一个不同但相似的问题,其中包含解决您问题的示例代码:Force close file by its path on Windows

您只需复制所有内容并将删除文件的部分替换为损坏文件的部分。希望对你有帮助:D

【讨论】:

【解决方案3】:

一般来说,接管文件锁所有权不是操作系统提供或应该做的事情,在没有原始锁所有者的帮助下也是如此。 (1) 您不能轻易绕过操作系统的安全保证,当然不能从您的用户级测试代码中绕过。

有关如何在原始所有者的积极协助下完成此操作的一些指示,请参阅 MSDN: Windows → Dev Center - Desktop → LockFileEx functionMSDN: Windows → Dev Center - Desktop → DuplicateHandle function 但这不太可能是您想要的方式。

SqLite 数据库锁定方法(您的应用程序使用)的一些描述在http://www.sqlite.org/lockingv3.html 中进行了描述,尤其是“如何损坏数据库文件”一章说

..显然,将不正确的数据引入数据库文件或日志中间的硬件或操作系统故障将导致问题。同样,如果恶意进程打开数据库文件或日志并将格式错误的数据写入其中,则数据库将损坏。对于这类问题无能为力,因此不再给予更多关注..

如果您真的想模拟事情如何变得非常错误,那么一种方法是 (2) 构建自定义的仅测试 SqLite,而 pager module (http://www.sqlite.org/src/finfo?name=src/pager.c) 受到损害,SqLite 具有源代码可用,因此可以定制

(3) 最常用的理论是,对于您的测试场景,您应该使用配备Mock objects 的特殊测试环境来伪造您想要测试的行为。在这样的测试环境中,您的应用程序将在特殊的“正在测试”模式下编译,在该模式下,应用程序会在被要求这样做时自愿暂时释放SqLite 连接,以便环境可能模拟操作系统或硬件故障,然后继续。

在您的应用程序中使用模拟对象容易得多然后例如在黑盒环境中伪造错误的文件系统驱动程序。

相关:http://en.wikipedia.org/wiki/Software_testing#Destructive_testing

【讨论】:

    猜你喜欢
    • 2010-09-05
    • 1970-01-01
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-29
    相关资源
    最近更新 更多