├── README.md ├── Veeamon.cpp ├── VeeamonBasic.cpp └── veeamfsr.sys /README.md: -------------------------------------------------------------------------------- 1 | # veeamon 2 | 3 | The code demonstrates usage of VeeamFSR.sys to control IO operations on a folder. Run 'veeamon NativePathToFolder' to start folder monitoring. More details in the blogpost: https://zwclose.github.io/veeamon/ 4 | -------------------------------------------------------------------------------- /Veeamon.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | enum NameEntryType 8 | { 9 | ProcessEntry, 10 | MonitoredEntry, 11 | FileEntry 12 | }; 13 | 14 | enum Operations_222440 : DWORD 15 | { 16 | Op10 = 0x10, 17 | Op11 = 0x11, 18 | Op12 = 0x12, 19 | Op13 = 0x13, 20 | Op14 = 0x14, 21 | Op15 = 0x15, 22 | Op20 = 0x20, 23 | Op21 = 0x21, 24 | Op22 = 0x22, 25 | Op30 = 0x30, 26 | Op31 = 0x31, 27 | }; 28 | 29 | 30 | enum RequestFlags : BYTE 31 | { 32 | RF_CallPreHandler = 0x1, 33 | RF_CallPostHandler = 0x2, 34 | RF_PassDown = 0x10, 35 | RF_Wait = 0x20, 36 | RF_DenyAccess = 0x40, 37 | RF_CompleteRequest = 0x80, 38 | }; 39 | 40 | struct CtrlBlock 41 | { 42 | BYTE ProcessIndex; 43 | BYTE FolderIndex; 44 | WORD FileIndex : 10; 45 | WORD MajorFunction : 6; 46 | }; 47 | 48 | struct SharedBufferEntry 49 | { 50 | //header 51 | DWORD Flags; 52 | union 53 | { 54 | CtrlBlock Ctrl; 55 | DWORD d1; 56 | }; 57 | 58 | //body 59 | DWORD d2; 60 | DWORD d3; 61 | 62 | DWORD d4; 63 | DWORD d5; 64 | DWORD d6; 65 | DWORD d7; 66 | }; 67 | 68 | struct SharedBufferDescriptor 69 | { 70 | DWORD FolderIndex; 71 | DWORD SharedBufferLength; 72 | DWORD SharedBufferPtr; 73 | DWORD Unk; 74 | }; 75 | 76 | SharedBufferDescriptor SharedBufDesc; 77 | 78 | #define IOCTL_START_FOLDER_MONITORING CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x222400 79 | #define IOCTL_STOP_FOLDER_MONITORING CTL_CODE(FILE_DEVICE_UNKNOWN, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x222404 80 | #define IOCTL_UNWAIT_REQUEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x920, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x222480 81 | #define IOCTL_SET_STREAM_FLAGS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x910, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x222440 82 | 83 | struct FlagsDescritptor 84 | { 85 | BYTE Function; 86 | RequestFlags RFlags; 87 | }; 88 | 89 | DWORD FileMapping[0x80]; 90 | DWORD ProcessMapping[0x80]; 91 | 92 | BOOL CtlDestroyFolder(HANDLE hDevice, DWORD FolderIndex) 93 | { 94 | DWORD BytesReturned; 95 | 96 | BOOL r = DeviceIoControl(hDevice, IOCTL_STOP_FOLDER_MONITORING, &FolderIndex, sizeof(FolderIndex), 0, 0, &BytesReturned, 0); 97 | if (r == FALSE) 98 | { 99 | printf("DestroyFolder failed\n"); 100 | } 101 | return r; 102 | } 103 | 104 | BOOL CtlCreateMonitoredFolder( 105 | HANDLE hDevice, 106 | PCWCHAR FolderPathName, 107 | PHANDLE SharedBufSemaphore, 108 | PHANDLE NewEntrySemaphore 109 | ) 110 | { 111 | DWORD BytesReturned; 112 | 113 | struct MonitoredFolder 114 | { 115 | HANDLE SharedBufSemaphore; 116 | DWORD d1; 117 | HANDLE NewEntrySemaphore; 118 | DWORD d2; 119 | DWORD f1; //+0x10 120 | DWORD SharedBufferEntriesCount; //+0x14 121 | DWORD PathLength; //+0x18 122 | WCHAR PathName[0x80]; //+0x1C 123 | }; 124 | 125 | MonitoredFolder Folder = { }; 126 | //const wchar_t *pFolderPath = L"\\Device\\HardDiskVolume1\\TMP\\"; 127 | 128 | *SharedBufSemaphore = *NewEntrySemaphore = nullptr; 129 | // Add protected folder 130 | Folder.SharedBufSemaphore = CreateSemaphoreW(nullptr, 0, 10000, nullptr); 131 | Folder.NewEntrySemaphore = CreateSemaphoreW(nullptr, 1, 10000, nullptr); 132 | Folder.f1 = 0x20; 133 | Folder.SharedBufferEntriesCount = 0x200; 134 | Folder.PathLength = wcslen(FolderPathName) * sizeof(FolderPathName[0]); 135 | wcscpy(Folder.PathName, FolderPathName); 136 | BOOL r = DeviceIoControl(hDevice, IOCTL_START_FOLDER_MONITORING, &Folder, sizeof(Folder), &SharedBufDesc, sizeof(SharedBufDesc), &BytesReturned, 0); 137 | if (r == FALSE) 138 | { 139 | printf("CreateFolder failed\n"); 140 | } 141 | else 142 | { 143 | *SharedBufSemaphore = Folder.SharedBufSemaphore; 144 | *NewEntrySemaphore = Folder.NewEntrySemaphore; 145 | if (BytesReturned != sizeof(SharedBufDesc)) 146 | { 147 | printf("Bad sizeof shared buffer\n"); 148 | r = FALSE; 149 | } 150 | } 151 | return r; 152 | } 153 | 154 | BOOL CtlUnwaitRequest( 155 | HANDLE hDevice, 156 | CtrlBlock* Ctrl, 157 | WORD SharedBufferEntryIndex, 158 | RequestFlags RFlags 159 | ) 160 | { 161 | struct UnwaitDescriptor 162 | { 163 | CtrlBlock Ctrl; 164 | 165 | DWORD SharedBufferEntryIndex; 166 | RequestFlags RFlags; 167 | BYTE IsStatusPresent; 168 | BYTE IsUserBufferPresent; 169 | BYTE SetSomeFlag; 170 | DWORD Status; 171 | DWORD Information; 172 | PVOID UserBuffer; 173 | DWORD d6; 174 | DWORD UserBufferLength; 175 | }; 176 | 177 | DWORD BytesReturned; 178 | UnwaitDescriptor Unwait = { 0, }; 179 | 180 | Unwait.Ctrl.FolderIndex = Ctrl->FolderIndex; 181 | Unwait.Ctrl.MajorFunction = Ctrl->MajorFunction; 182 | Unwait.Ctrl.FileIndex = Ctrl->FileIndex; 183 | Unwait.SharedBufferEntryIndex = SharedBufferEntryIndex; 184 | Unwait.RFlags = RFlags; 185 | 186 | Unwait.IsUserBufferPresent = 0; 187 | 188 | // Uncomment the code below to crash the OS. 189 | // VeeamFSR doesn't handle this parameter correctly. Setting IsUserBuffPresent to true 190 | // leads to double free in the completion rountine. 191 | //Unwait.UserBuffer = (PVOID)"aaaabbbb"; 192 | //Unwait.UserBufferLength = 8; 193 | //Unwait.IsUserBufferPresent = 1; 194 | 195 | 196 | BOOL r = DeviceIoControl(hDevice, IOCTL_UNWAIT_REQUEST, &Unwait, sizeof(Unwait), 0, 0, &BytesReturned, 0); 197 | if (r == FALSE) 198 | { 199 | printf("UnwaitRequest failed\n"); 200 | } 201 | return r; 202 | } 203 | 204 | BOOL CtlUnwaitRequest( 205 | HANDLE hDevice, 206 | CtrlBlock* Ctrl, 207 | WORD SharedAreaEntryIndex 208 | ) 209 | { 210 | return CtlUnwaitRequest(hDevice, Ctrl, SharedAreaEntryIndex, RF_PassDown); 211 | } 212 | 213 | BOOL CtlSetStreamFlags(HANDLE hDevice, CtrlBlock* Ctrl, FlagsDescritptor FlagsDescs[], DWORD FlagsCount) 214 | { 215 | struct SubDataDescr 216 | { 217 | BYTE SubDataCount; 218 | BYTE Flags; 219 | WORD SubDataOffset; 220 | }; 221 | 222 | struct SubData 223 | { 224 | DWORD d0; 225 | BYTE Flags; 226 | }; 227 | 228 | #pragma pack(push, 1) 229 | struct StreamFlagDescriptor 230 | { 231 | BYTE Unk0; //Should be 1 232 | RequestFlags RFlags; 233 | BYTE Unk1; //Should be 28 * 4 + 8 234 | BYTE Unk2; //Shoulfd be 0 235 | }; 236 | 237 | struct StreamFlags 238 | { 239 | CtrlBlock Ctrl; 240 | DWORD Operation; 241 | StreamFlagDescriptor StreamFlags[30]; //IRP_MJ_MAXIMUM_FUNCTION + 2 242 | BYTE Bytes[5]; 243 | }; 244 | #pragma pack(pop) 245 | 246 | StreamFlags Flags{}; 247 | Flags.Ctrl.ProcessIndex = Ctrl->ProcessIndex; 248 | Flags.Ctrl.FileIndex = Ctrl->FileIndex; 249 | Flags.Ctrl.FolderIndex = Ctrl->FolderIndex; 250 | Flags.Operation = Op15; 251 | 252 | Flags.StreamFlags[0].Unk0 = 1; 253 | Flags.StreamFlags[0].RFlags = RF_Wait; 254 | Flags.StreamFlags[0].Unk1 = 28 * 4 + 8; 255 | Flags.StreamFlags[0].Unk2 = 0; 256 | for (int i = 1; i < 29 /*IRP_MJ_MAXIMUM_FUNCTION + 2*/; i++) 257 | { 258 | Flags.StreamFlags[i].Unk0 = 1; 259 | Flags.StreamFlags[i].RFlags = RF_PassDown; 260 | Flags.StreamFlags[i].Unk1 = 28 * 4 + 8; 261 | Flags.StreamFlags[i].Unk2 = 0; 262 | } 263 | Flags.Bytes[0] = 0xB0; 264 | Flags.Bytes[1] = 0xBA; 265 | Flags.Bytes[2] = 0xFE; 266 | Flags.Bytes[3] = 0xCA; 267 | Flags.Bytes[4] = 0x11; 268 | 269 | for (unsigned int i = 0; i < FlagsCount; i++) 270 | { 271 | BYTE Function = FlagsDescs[i].Function; 272 | Flags.StreamFlags[Function].RFlags = FlagsDescs[i].RFlags; 273 | } 274 | 275 | DWORD BytesReturned; 276 | BYTE Out[0x34]; 277 | BOOL r = DeviceIoControl(hDevice, IOCTL_SET_STREAM_FLAGS, &Flags, sizeof(Flags), Out, sizeof(Out), &BytesReturned, 0); 278 | if (r == FALSE) 279 | { 280 | printf("CtlSetStreamFlags failed\n"); 281 | } 282 | return r; 283 | } 284 | 285 | DWORD GetProcessImageFileNameByPid(DWORD Pid, PWCHAR pBuf, DWORD Length) 286 | { 287 | DWORD r = 0; 288 | HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, Pid); 289 | if (hProcess == NULL) 290 | { 291 | printf("OpenProcess failed with %d for pid %d\n", GetLastError(), Pid); 292 | } 293 | else 294 | { 295 | r = GetProcessImageFileNameW(hProcess, pBuf, Length); 296 | if (r == 0) 297 | { 298 | printf("GetProcessImageFileNameW failed: %d\n", GetLastError()); 299 | } 300 | CloseHandle(hProcess); 301 | } 302 | 303 | return r; 304 | } 305 | 306 | 307 | BOOL IsEqualPathName(SharedBufferEntry* NameEntry, const PCWCHAR PathName) 308 | { 309 | size_t ProtectedFileNameLength = wcslen(PathName) * sizeof(PathName[0]); 310 | ProtectedFileNameLength += sizeof(PathName[0]); //including null termination 311 | 312 | return (_wcsnicmp(PathName, (wchar_t*)NameEntry->d6, NameEntry->d4) == 0); 313 | } 314 | 315 | VOID PrintEntryInfo(const CHAR* pOpName, SharedBufferEntry IOEntryBuffer[], SharedBufferEntry* pEntry) 316 | { 317 | WCHAR ProcessName[MAX_PATH]; 318 | DWORD ProcessIndex = ProcessMapping[pEntry->Ctrl.ProcessIndex]; 319 | DWORD Pid = IOEntryBuffer[ProcessIndex].d6; 320 | DWORD ProcessNameLength = GetProcessImageFileNameByPid(Pid, ProcessName, MAX_PATH); 321 | DWORD NameIndex = FileMapping[pEntry->Ctrl.FileIndex]; 322 | printf("%s for %ls by process %d (%ls)\n", pOpName, (PWSTR)IOEntryBuffer[NameIndex].d6, Pid, ProcessNameLength == 0 ? L"null" : ProcessName); 323 | } 324 | 325 | VOID PrintBuffer(PBYTE pBuffer, DWORD Length) 326 | { 327 | printf("Dumping buffer (0x80 max):\n"); 328 | if (Length > 0x80) 329 | { 330 | Length = 0x80; 331 | } 332 | for (unsigned int i = 0; i < Length; i++) 333 | { 334 | if ((i & 7) == 0) 335 | { 336 | printf("\n"); 337 | } 338 | if (IsCharAlphaNumericA(pBuffer[i]) == TRUE) 339 | { 340 | printf(" %c ", pBuffer[i]); 341 | } 342 | else 343 | { 344 | printf("%02x ", pBuffer[i]); 345 | } 346 | } 347 | printf("\n"); 348 | } 349 | 350 | int wmain(int arc, wchar_t** argv) 351 | { 352 | if (arc != 2) 353 | { 354 | printf("Usage: veeamon NativePathToFolder\n"); 355 | return -1; 356 | } 357 | 358 | HANDLE hDevice = CreateFileW(L"\\\\.\\VeeamFSR", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, 0); 359 | if (hDevice == INVALID_HANDLE_VALUE) 360 | { 361 | printf("CreateFileW: %d\n", GetLastError()); 362 | return -1; 363 | } 364 | 365 | HANDLE SharedBufSemaphore; 366 | HANDLE NewEntrySemaphore; 367 | WORD CurrEntry = 0; 368 | 369 | PCWCHAR Folder = argv[1]; 370 | if (CtlCreateMonitoredFolder( 371 | hDevice, 372 | Folder, 373 | &SharedBufSemaphore, 374 | &NewEntrySemaphore) == FALSE) 375 | { 376 | printf("Failed setting up monitored folder\n"); 377 | return -1; 378 | } 379 | 380 | printf("Set up monitor on %ls\n", Folder); 381 | printf("FolderIndex: 0x%x\n", SharedBufDesc.FolderIndex); 382 | printf("Shared buffer: %p\n", (PVOID)SharedBufDesc.SharedBufferPtr); 383 | printf("Shared buffer length: 0x%x\n", SharedBufDesc.SharedBufferLength); 384 | printf("Uknown: 0x%x\n", SharedBufDesc.Unk); 385 | printf("\nStarting IO loop\n"); 386 | 387 | SharedBufferEntry* IOEntryBuffer = (SharedBufferEntry*)SharedBufDesc.SharedBufferPtr; 388 | SharedBufferEntry* IOEntry; 389 | 390 | PCWCHAR ProtectedName = L"\\Device\\HarddiskVolume1\\tmp\\Cthon98.txt"; 391 | PCWCHAR FakeReadName = L"\\Device\\HarddiskVolume1\\tmp\\AzureDiamond.txt"; 392 | for (;;) 393 | { 394 | LONG l; 395 | 396 | ReleaseSemaphore(NewEntrySemaphore, 1, &l); 397 | WaitForSingleObject(SharedBufSemaphore, INFINITE); 398 | 399 | printf("Entry #%d\n", CurrEntry); 400 | 401 | IOEntry = &IOEntryBuffer[CurrEntry]; 402 | switch (IOEntry->Ctrl.MajorFunction) 403 | { 404 | // 405 | // Special entry handlers 406 | // 407 | case 0x37: //Name entry 408 | { 409 | printf("\tADD\n"); 410 | 411 | switch (IOEntry->d2) 412 | { 413 | case ProcessEntry: 414 | printf("\tprocess: %d\n", IOEntry->d6); 415 | ProcessMapping[IOEntry->d3] = CurrEntry; 416 | break; 417 | case FileEntry: 418 | //.d4 == length 419 | printf("\tfile: %ls\n", (PWSTR)IOEntry->d6); 420 | FileMapping[IOEntry->d3] = CurrEntry; 421 | break; 422 | case MonitoredEntry: 423 | //.d4 == length 424 | printf("\tmonitored dir: %ls\n", (PWSTR)IOEntry->d6); 425 | break; 426 | } 427 | 428 | break; 429 | } 430 | case 0x38: 431 | { 432 | printf("\tDELETION\n"); 433 | switch (IOEntry->d2) 434 | { 435 | case ProcessEntry: 436 | printf("\tprocess\n"); 437 | break; 438 | case FileEntry: 439 | printf("\tfile\n"); 440 | break; 441 | case MonitoredEntry: 442 | printf("\tmonitored dir\n"); 443 | break; 444 | } 445 | printf("\tindex: %d\n", IOEntry->d2); 446 | 447 | break; 448 | } 449 | case 0x39: 450 | { 451 | printf("\tCOMPLETION of IRP_MJ_%d, index = %d, status = 0x%x, information: 0x%x\n", 452 | IOEntry->d2, 453 | IOEntry->d3, 454 | IOEntry->d4, 455 | IOEntry->d5); 456 | 457 | break; 458 | } 459 | case 0x3A: 460 | { 461 | printf("\tWRITE-related entry\n"); 462 | break; 463 | } 464 | // 465 | // IRP_MJ_XXX and FastIo handlers 466 | // 467 | case 0x0: //IRP_MJ_CREATE 468 | case 0x33: //Fast _IRP_MJ_CREATE 469 | { 470 | PrintEntryInfo("IRP_MJ_CREATE", IOEntryBuffer, IOEntry); 471 | 472 | DWORD EntryNameIndex = FileMapping[IOEntry->Ctrl.FileIndex]; 473 | if (IsEqualPathName(&IOEntryBuffer[EntryNameIndex], ProtectedName)) 474 | { 475 | printf("Denying access to %ls\n", ProtectedName); 476 | CtlUnwaitRequest(hDevice, &IOEntry->Ctrl, CurrEntry, RF_DenyAccess); 477 | break; 478 | } 479 | 480 | FlagsDescritptor FlagsDescs[2]; 481 | if (IsEqualPathName(&IOEntryBuffer[EntryNameIndex], FakeReadName)) 482 | { 483 | FlagsDescs[0].Function = 3; //IRP_MJ_READ 484 | FlagsDescs[0].RFlags = RF_CompleteRequest; 485 | FlagsDescs[1].Function = 4; //IRP_MJ_WRITE 486 | FlagsDescs[1].RFlags = (RequestFlags)(RF_PassDown | RF_CallPreHandler); 487 | } 488 | else 489 | { 490 | FlagsDescs[0].Function = 3; //IRP_MJ_READ 491 | FlagsDescs[0].RFlags = (RequestFlags)(RF_PassDown | RF_CallPostHandler); 492 | FlagsDescs[1].Function = 4; //IRP_MJ_WRITE 493 | FlagsDescs[1].RFlags = (RequestFlags)(RF_PassDown | RF_CallPreHandler); 494 | } 495 | CtlSetStreamFlags(hDevice, &IOEntry->Ctrl, FlagsDescs, 2); 496 | 497 | CtlUnwaitRequest(hDevice, &IOEntry->Ctrl, CurrEntry, RF_PassDown); 498 | 499 | break; 500 | } 501 | case 0x3: //IRP_MJ_READ 502 | case 0x1D: //Fast IRP_MJ_READ 503 | { 504 | PrintEntryInfo("IRP_MJ_READ", IOEntryBuffer, IOEntry); 505 | 506 | DWORD Length = IOEntry->d5; 507 | PBYTE Buffer = (PBYTE)IOEntry->d6; 508 | DWORD EntryNameIndex = FileMapping[IOEntry->Ctrl.FileIndex]; 509 | if (IsEqualPathName(&IOEntryBuffer[EntryNameIndex], FakeReadName) == FALSE) 510 | { 511 | PrintBuffer(Buffer, Length); 512 | } 513 | else 514 | { 515 | printf("Faking read buffer with '*' for %ls\n", FakeReadName); 516 | for (unsigned int i = 0; i < Length; i++) 517 | { 518 | Buffer[i] = '*'; 519 | } 520 | PrintBuffer(Buffer, Length); 521 | CtlUnwaitRequest(hDevice, &IOEntry->Ctrl, CurrEntry, RF_CompleteRequest); 522 | } 523 | 524 | break; 525 | } 526 | case 0x4: //IRP_MJ_WRITE 527 | case 0x1E: //Fast IRP_MJ_WRITE 528 | { 529 | PrintEntryInfo("IRP_MJ_WRITE", IOEntryBuffer, &IOEntryBuffer[CurrEntry]); 530 | 531 | DWORD Length = IOEntry->d5; 532 | PBYTE Buffer = (PBYTE)IOEntry->d6; 533 | PrintBuffer(Buffer, Length); 534 | 535 | break; 536 | } 537 | default: 538 | { 539 | CHAR OpName[40]{}; 540 | sprintf_s(OpName, 40, "IRP_MJ_%d", IOEntry->Ctrl.MajorFunction); 541 | PrintEntryInfo(OpName, IOEntryBuffer, &IOEntryBuffer[CurrEntry]); 542 | 543 | break; 544 | } 545 | } 546 | 547 | printf("\t0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", IOEntry->Flags, IOEntry->d1, IOEntry->d2, IOEntry->d3); 548 | printf("\t0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", IOEntry->d4, IOEntry->d5, IOEntry->d6, IOEntry->d7); 549 | 550 | CurrEntry++; 551 | if (CurrEntry >= 0x200) 552 | { 553 | break; 554 | } 555 | } 556 | 557 | CtlDestroyFolder(hDevice, 0); 558 | CloseHandle(hDevice); 559 | 560 | printf("Press any key...\n"); 561 | getchar(); 562 | 563 | return 0; 564 | } 565 | 566 | -------------------------------------------------------------------------------- /VeeamonBasic.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | enum NameEntryType 8 | { 9 | ProcessEntry, 10 | MonitoredEntry, 11 | FileEntry 12 | }; 13 | 14 | enum RequestFlags : BYTE 15 | { 16 | RF_CallPreHandler = 0x1, 17 | RF_CallPostHandler = 0x2, 18 | RF_PassDown = 0x10, 19 | RF_Wait = 0x20, 20 | RF_DenyAccess = 0x40, 21 | RF_CompleteRequest = 0x80, 22 | }; 23 | 24 | struct CtrlBlock 25 | { 26 | BYTE ProcessIndex; 27 | BYTE FolderIndex; 28 | WORD FileIndex : 10; 29 | WORD MajorFunction : 6; 30 | }; 31 | 32 | struct SharedBufferEntry 33 | { 34 | //header 35 | DWORD Flags; 36 | union 37 | { 38 | CtrlBlock Ctrl; 39 | DWORD d1; 40 | }; 41 | 42 | //body 43 | DWORD d2; 44 | DWORD d3; 45 | 46 | DWORD d4; 47 | DWORD d5; 48 | DWORD d6; 49 | DWORD d7; 50 | }; 51 | 52 | struct SharedBufferDescriptor 53 | { 54 | DWORD FolderIndex; 55 | DWORD SharedBufferLength; 56 | DWORD SharedBufferPtr; 57 | DWORD Unk; 58 | }; 59 | 60 | SharedBufferDescriptor SharedBufDesc; 61 | 62 | #define IOCTL_START_FOLDER_MONITORING CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x222400 63 | #define IOCTL_STOP_FOLDER_MONITORING CTL_CODE(FILE_DEVICE_UNKNOWN, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x222404 64 | #define IOCTL_UNWAIT_REQUEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x920, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x222480 65 | #define IOCTL_SET_STREAM_FLAGS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x910, METHOD_BUFFERED, FILE_ANY_ACCESS) //0x222440 66 | 67 | BOOL CtlDestroyFolder(HANDLE hDevice, DWORD FolderIndex) 68 | { 69 | DWORD BytesReturned; 70 | 71 | BOOL r = DeviceIoControl(hDevice, IOCTL_STOP_FOLDER_MONITORING, &FolderIndex, sizeof(FolderIndex), 0, 0, &BytesReturned, 0); 72 | if (r == FALSE) 73 | { 74 | printf("DestroyFolder failed\n"); 75 | } 76 | return r; 77 | } 78 | 79 | BOOL CtlCreateMonitoredFolder( 80 | HANDLE hDevice, 81 | PCWCHAR FolderPathName, 82 | PHANDLE SharedBufSemaphore, 83 | PHANDLE NewEntrySemaphore 84 | ) 85 | { 86 | DWORD BytesReturned; 87 | 88 | struct MonitoredFolder 89 | { 90 | HANDLE SharedBufSemaphore; 91 | DWORD d1; 92 | HANDLE NewEntrySemaphore; 93 | DWORD d2; 94 | DWORD f1; //+0x10 95 | DWORD SharedBufferEntriesCount; //+0x14 96 | DWORD PathLength; //+0x18 97 | WCHAR PathName[0x80]; //+0x1C 98 | }; 99 | 100 | MonitoredFolder Folder = { }; 101 | //const wchar_t *pFolderPath = L"\\Device\\HardDiskVolume1\\TMP\\"; 102 | 103 | *SharedBufSemaphore = *NewEntrySemaphore = nullptr; 104 | // Add protected folder 105 | Folder.SharedBufSemaphore = CreateSemaphoreW(nullptr, 0, 10000, nullptr); 106 | Folder.NewEntrySemaphore = CreateSemaphoreW(nullptr, 1, 10000, nullptr); 107 | Folder.f1 = 0x20; 108 | Folder.SharedBufferEntriesCount = 0x200; 109 | Folder.PathLength = wcslen(FolderPathName) * sizeof(FolderPathName[0]); 110 | wcscpy(Folder.PathName, FolderPathName); 111 | BOOL r = DeviceIoControl(hDevice, IOCTL_START_FOLDER_MONITORING, &Folder, sizeof(Folder), &SharedBufDesc, sizeof(SharedBufDesc), &BytesReturned, 0); 112 | if (r == FALSE) 113 | { 114 | printf("CreateFolder failed\n"); 115 | } 116 | else 117 | { 118 | *SharedBufSemaphore = Folder.SharedBufSemaphore; 119 | *NewEntrySemaphore = Folder.NewEntrySemaphore; 120 | if (BytesReturned != sizeof(SharedBufDesc)) 121 | { 122 | printf("Bad sizeof shared buffer\n"); 123 | r = FALSE; 124 | } 125 | } 126 | return r; 127 | } 128 | 129 | BOOL CtlUnwaitRequest( 130 | HANDLE hDevice, 131 | CtrlBlock* Ctrl, 132 | WORD SharedBufferEntryIndex, 133 | RequestFlags RFlags 134 | ) 135 | { 136 | struct UnwaitDescriptor 137 | { 138 | CtrlBlock Ctrl; 139 | 140 | DWORD SharedBufferEntryIndex; 141 | RequestFlags RFlags; 142 | BYTE IsStatusPresent; 143 | BYTE IsUserModeBuffPresent; 144 | BYTE SetSomeFlag; 145 | DWORD Status; 146 | DWORD Information; 147 | PVOID UserBuff; 148 | DWORD d6; 149 | DWORD UserBuffLength; 150 | }; 151 | 152 | DWORD BytesReturned; 153 | UnwaitDescriptor Unwait = { 0, }; 154 | 155 | Unwait.Ctrl.FolderIndex = Ctrl->FolderIndex; 156 | Unwait.Ctrl.MajorFunction = Ctrl->MajorFunction; 157 | Unwait.Ctrl.FileIndex = Ctrl->FileIndex; 158 | Unwait.SharedBufferEntryIndex = SharedBufferEntryIndex; 159 | Unwait.RFlags = RFlags; 160 | 161 | Unwait.IsUserModeBuffPresent = 0; 162 | 163 | // Uncomment the code below to crash the OS. 164 | // VeeamFSR doesn't handle this parameter correctly. Setting IsUserModeBuffPresent to true 165 | // leads to double free in the completion rountine. 166 | //Unwait.UserBuff = (PVOID)"aaaabbbb"; 167 | //Unwait.UserBuffLength = 8; 168 | //Unwait.IsUserModeBuffPresent = 1; 169 | 170 | 171 | BOOL r = DeviceIoControl(hDevice, IOCTL_UNWAIT_REQUEST, &Unwait, sizeof(Unwait), 0, 0, &BytesReturned, 0); 172 | if (r == FALSE) 173 | { 174 | printf("UnwaitRequest failed\n"); 175 | } 176 | return r; 177 | } 178 | 179 | BOOL CtlUnwaitRequest( 180 | HANDLE hDevice, 181 | CtrlBlock* Ctrl, 182 | WORD SharedAreaEntryIndex 183 | ) 184 | { 185 | return CtlUnwaitRequest(hDevice, Ctrl, SharedAreaEntryIndex, RF_PassDown); 186 | } 187 | 188 | DWORD GetProcessImageFileNameByPid(DWORD Pid, PWCHAR pBuf, DWORD Length) 189 | { 190 | DWORD r = 0; 191 | HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, Pid); 192 | if (hProcess == NULL) 193 | { 194 | printf("OpenProcess failed with %d for pid %d\n", GetLastError(), Pid); 195 | } 196 | else 197 | { 198 | r = GetProcessImageFileNameW(hProcess, pBuf, Length); 199 | if (r == 0) 200 | { 201 | printf("GetProcessImageFileNameW failed: %d\n", GetLastError()); 202 | } 203 | CloseHandle(hProcess); 204 | } 205 | 206 | return r; 207 | } 208 | 209 | DWORD FileMapping[0x80]; 210 | DWORD ProcessMapping[0x80]; 211 | 212 | VOID PrintEntryInfo(const CHAR* pOpName, SharedBufferEntry IOEntryBuffer[], SharedBufferEntry* pEntry) 213 | { 214 | WCHAR ProcessName[MAX_PATH]; 215 | DWORD ProcessIndex = ProcessMapping[pEntry->Ctrl.ProcessIndex]; 216 | DWORD Pid = IOEntryBuffer[ProcessIndex].d6; 217 | DWORD ProcessNameLength = GetProcessImageFileNameByPid(Pid, ProcessName, MAX_PATH); 218 | DWORD NameIndex = FileMapping[pEntry->Ctrl.FileIndex]; 219 | printf("%s for %ls by process %d (%ls)\n", pOpName, (PWSTR)IOEntryBuffer[NameIndex].d6, Pid, ProcessNameLength == 0 ? L"null" : ProcessName); 220 | } 221 | 222 | VOID PrintBuffer(PBYTE pBuffer, DWORD Length) 223 | { 224 | for (int i = 0; i < 17; i++) 225 | { 226 | if ((i & 7) == 0) 227 | { 228 | printf("\n"); 229 | } 230 | if (IsCharAlphaNumericA(pBuffer[i]) == TRUE) 231 | { 232 | printf(" %c ", pBuffer[i]); 233 | } 234 | else 235 | { 236 | printf("%02x ", pBuffer[i]); 237 | } 238 | } 239 | printf("\n"); 240 | } 241 | 242 | int wmain(int arc, wchar_t** argv) 243 | { 244 | if (arc != 2) 245 | { 246 | printf("Usage: veeamon NativePathToFolder\n"); 247 | return -1; 248 | } 249 | 250 | HANDLE hDevice = CreateFileW(L"\\\\.\\VeeamFSR", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, 0); 251 | if (hDevice == INVALID_HANDLE_VALUE) 252 | { 253 | printf("CreateFileW: %d\n", GetLastError()); 254 | return -1; 255 | } 256 | 257 | HANDLE SharedBufSemaphore; 258 | HANDLE NewEntrySemaphore; 259 | WORD CurrEntry = 0; 260 | 261 | PCWCHAR Folder = argv[1]; 262 | if (CtlCreateMonitoredFolder( 263 | hDevice, 264 | Folder, 265 | &SharedBufSemaphore, 266 | &NewEntrySemaphore) == FALSE) 267 | { 268 | printf("Failed setting up monitored folder\n"); 269 | return -1; 270 | } 271 | 272 | printf("Set up monitor on %ls\n", Folder); 273 | printf("FolderIndex: 0x%x\n", SharedBufDesc.FolderIndex); 274 | printf("Shared buffer: %p\n", (PVOID)SharedBufDesc.SharedBufferPtr); 275 | printf("Shared buffer length: 0x%x\n", SharedBufDesc.SharedBufferLength); 276 | printf("Uknown: 0x%x\n", SharedBufDesc.Unk); 277 | printf("\nStarting IO loop\n"); 278 | 279 | SharedBufferEntry* IOEntryBuffer = (SharedBufferEntry*)SharedBufDesc.SharedBufferPtr; 280 | SharedBufferEntry* IOEntry; 281 | 282 | for (;;) 283 | { 284 | LONG l; 285 | 286 | ReleaseSemaphore(NewEntrySemaphore, 1, &l); 287 | WaitForSingleObject(SharedBufSemaphore, INFINITE); 288 | 289 | printf("Entry #%d\n", CurrEntry); 290 | 291 | IOEntry = &IOEntryBuffer[CurrEntry]; 292 | switch (IOEntry->Ctrl.MajorFunction) 293 | { 294 | // 295 | // IRP_MJ_XXX and FastIo handlers 296 | // 297 | case 0x0: //IRP_MJ_CREATE 298 | case 0x33: //Fast _IRP_MJ_CREATE 299 | { 300 | PrintEntryInfo("IRP_MJ_CREATE", IOEntryBuffer, IOEntry); 301 | CtlUnwaitRequest(hDevice, &IOEntry->Ctrl, CurrEntry, RF_PassDown); 302 | 303 | break; 304 | } 305 | default: 306 | { 307 | CHAR OpName[40]{}; 308 | sprintf_s(OpName, 40, "IRP_MJ_%d", IOEntry->Ctrl.MajorFunction); 309 | PrintEntryInfo(OpName, IOEntryBuffer, &IOEntryBuffer[CurrEntry]); 310 | 311 | break; 312 | } 313 | 314 | 315 | // 316 | // Special entry handlers 317 | // 318 | case 0x37: //Name entry 319 | { 320 | printf("\tADD\n"); 321 | 322 | switch (IOEntry->d2) 323 | { 324 | case ProcessEntry: 325 | printf("\tprocess: %d\n", IOEntry->d6); 326 | ProcessMapping[IOEntry->d3] = CurrEntry; 327 | break; 328 | case FileEntry: 329 | //.d4 == length 330 | printf("\tfile: %ls\n", (PWSTR)IOEntry->d6); 331 | FileMapping[IOEntry->d3] = CurrEntry; 332 | break; 333 | case MonitoredEntry: 334 | //.d4 == length 335 | printf("\tmonitored dir: %ls\n", (PWSTR)IOEntry->d6); 336 | break; 337 | } 338 | 339 | break; 340 | } 341 | case 0x38: 342 | { 343 | printf("\tDELETION\n"); 344 | switch (IOEntry->d2) 345 | { 346 | case ProcessEntry: 347 | printf("\tprocess\n"); 348 | break; 349 | case FileEntry: 350 | printf("\tfile\n"); 351 | break; 352 | case MonitoredEntry: 353 | printf("\tmonitored dir\n"); 354 | break; 355 | } 356 | printf("\tindex: %d\n", IOEntry->d2); 357 | 358 | break; 359 | } 360 | case 0x39: 361 | { 362 | printf("\tCOMPLETION of IRP_MJ_%d, index = %d, status = 0x%x, information: 0x%x\n", 363 | IOEntry->d2, 364 | IOEntry->d3, 365 | IOEntry->d4, 366 | IOEntry->d5); 367 | 368 | break; 369 | } 370 | case 0x3A: 371 | { 372 | printf("\tWRITE-related entry\n"); 373 | break; 374 | } 375 | } 376 | 377 | printf("\t0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", IOEntry->Flags, IOEntry->d1, IOEntry->d2, IOEntry->d3); 378 | printf("\t0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", IOEntry->d4, IOEntry->d5, IOEntry->d6, IOEntry->d7); 379 | 380 | CurrEntry++; 381 | if (CurrEntry >= 0x200) 382 | { 383 | break; 384 | } 385 | } 386 | 387 | CtlDestroyFolder(hDevice, 0); 388 | CloseHandle(hDevice); 389 | 390 | printf("Press any key...\n"); 391 | getchar(); 392 | 393 | return 0; 394 | } 395 | -------------------------------------------------------------------------------- /veeamfsr.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zwclose/veeamon/37b38948ffda7b8c1b173f3479e619c29ff43c82/veeamfsr.sys --------------------------------------------------------------------------------