2

While testing an application stability I am trying to remove/corrupt database used by the application. OS is Windows, file system is NTFS, tests language is C++.

What I need is to paste anything into the database file or remove it. But it is locked by the tested application during the run. So maybe someone faced this case before and you can give me/us an example on possible ways to bypass this lock in test code? Theory or source - I am appreciated for any kind of help.

2
  • What database are we talking about? (BTW: consider retagging your question with windows and testing to address more specific audience) Commented Oct 21, 2014 at 9:39
  • That's sqlite. But I think it does not matter even if that is a text file. All I need is somehow bypass the lock and corrupt the used file by appending random bytes or removing those. Commented Oct 21, 2014 at 10:16

3 Answers 3

3
#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); } 
Sign up to request clarification or add additional context in comments.

3 Comments

This one is for Windows 8.1. If you have another OS version, experiment with hFirstEntry[i].ObjectType == 30 . It should be 28 for previous versions
How do you call it? (a sample call would be great)
program.exe <process_pid> But this code was for Windows 8.1 back then. In 10 I am pretty sure you will need to experiment with hFirstEntry[i].ObjectType == 30 and change that 30 to something else (try 29, 31, 32). It is (or actually back then it was) undocumented API of the Windows so hard to find this description
2

You'll have to close the handle that your application has to the file. Keep in mind that you'll have to open your file again on your application, I believe.

I found a different but similar question, with sample code that solves your problem: Force close file by its path on Windows

You just need to copy everything and substitute the part that deletes the file for something that corrupts it. I hope it helps :D

3 Comments

Thanks to you found this one useful part: code.msdn.microsoft.com/windowsapps/CppFileHandle-03c8ea0b/…
I'm glad it helped you finding what you were looking for :D
Thanks to you @SlySherZ the final code is in my last answer to this question.
2

In general taking-over file lock ownership is not what Operating Systems provide or are supposed to do, not without the assistance of the original lock owner. (1) You can not easily bypass OS security guarantees, certainly not from your user-level testing code.

For some pointers on how this might be done with active assistance of the original owner see MSDN: Windows → Dev Center - Desktop → LockFileEx function and MSDN: Windows → Dev Center - Desktop → DuplicateHandle function but this is not likely the way you'd want to go.

Some description of the SqLite database locking approach (that your application uses) is described in http://www.sqlite.org/lockingv3.html and especially chapter "How To Corrupt Your Database Files" says

..Clearly, a hardware or operating system fault that introduces incorrect data into the middle of the database file or journal will cause problems. Likewise, if a rogue process opens a database file or journal and writes malformed data into the middle of it, then the database will become corrupt. There is not much that can be done about these kinds of problems so they are given no further attention..

If you really want to simulate how things get very wrong then one way is to (2) build custom test-only SqLite with the pager module (http://www.sqlite.org/src/finfo?name=src/pager.c) compromised, SqLite has the source code available so customization is possible

(3) The most commonly used theory says that for your testing scenarios you should use special testing environments equiped with Mock objects faking the behavior you want to test. In such a testing environment your application would be compiled in a special "under test" mode, where the application would voluntarily temporarily release the SqLite connection when asked to do so, so that the environment might simulate the OS or hardware failure and then continue.

Using mock objects within your application is much easier then e.g. faking a faulty file system driver in a black-box environment.

Relevant: http://en.wikipedia.org/wiki/Software_testing#Destructive_testing

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.