Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1670 lines
45 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. LogFileChanges.cpp
  5. Abstract:
  6. This AppVerifier shim hooks all the native file I/O APIs
  7. that change the state of the system and logs their
  8. associated data to a text file.
  9. Notes:
  10. This is a general purpose shim.
  11. History:
  12. 08/17/2001 rparsons Created
  13. 09/20/2001 rparsons Output attributes in XML
  14. VLOG with log file location
  15. --*/
  16. #include "precomp.h"
  17. #include "rtlutils.h"
  18. IMPLEMENT_SHIM_BEGIN(LogFileChanges)
  19. #include "ShimHookMacro.h"
  20. #include "LogFileChanges.h"
  21. BEGIN_DEFINE_VERIFIER_LOG(LogFileChanges)
  22. VERIFIER_LOG_ENTRY(VLOG_LOGFILECHANGES_LOGLOC)
  23. VERIFIER_LOG_ENTRY(VLOG_LOGFILECHANGES_UFW)
  24. END_DEFINE_VERIFIER_LOG(LogFileChanges)
  25. INIT_VERIFIER_LOG(LogFileChanges);
  26. //
  27. // Stores the NT path to the file system log file for the current session.
  28. //
  29. UNICODE_STRING g_strLogFilePath;
  30. //
  31. // Stores the DOS path to the file system log file for the current session.
  32. // This doesn't get freed.
  33. //
  34. LPWSTR g_pwszLogFilePath;
  35. //
  36. // Head of our doubly linked list.
  37. //
  38. LIST_ENTRY g_OpenHandleListHead;
  39. //
  40. // Stores the settings for our shim.
  41. //
  42. DWORD g_dwSettings;
  43. //
  44. // Global buffer for putting text into the XML.
  45. //
  46. WCHAR g_wszXMLBuffer[MAX_ELEMENT_SIZE];
  47. /*++
  48. Writes an entry to the log file.
  49. --*/
  50. void
  51. WriteEntryToLog(
  52. IN LPCWSTR pwszEntry
  53. )
  54. {
  55. int nLen = 0;
  56. HANDLE hFile;
  57. OBJECT_ATTRIBUTES ObjectAttributes;
  58. IO_STATUS_BLOCK IoStatusBlock;
  59. LARGE_INTEGER liOffset;
  60. NTSTATUS status;
  61. //
  62. // Note that we have to use native APIs throughout this function
  63. // to avoid a problem with circular hooking. That is, if we simply
  64. // call WriteFile, which is exported from kernel32, it will call NtWriteFile,
  65. // which is a call that we hook, in turn leaving us in and endless loop.
  66. //
  67. //
  68. // Attempt to get a handle to our log file.
  69. //
  70. InitializeObjectAttributes(&ObjectAttributes,
  71. &g_strLogFilePath,
  72. OBJ_CASE_INSENSITIVE,
  73. NULL,
  74. NULL);
  75. status = NtCreateFile(&hFile,
  76. FILE_APPEND_DATA | SYNCHRONIZE,
  77. &ObjectAttributes,
  78. &IoStatusBlock,
  79. 0,
  80. FILE_ATTRIBUTE_NORMAL,
  81. 0,
  82. FILE_OPEN,
  83. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  84. NULL,
  85. 0);
  86. if (!NT_SUCCESS(status)) {
  87. DPFN(eDbgLevelError, "[WriteEntryToLog] Failed to open log");
  88. return;
  89. }
  90. //
  91. // Write the data out to the file.
  92. //
  93. nLen = wcslen(pwszEntry);
  94. IoStatusBlock.Status = 0;
  95. IoStatusBlock.Information = 0;
  96. liOffset.LowPart = 0;
  97. liOffset.HighPart = 0;
  98. status = NtWriteFile(hFile,
  99. NULL,
  100. NULL,
  101. NULL,
  102. &IoStatusBlock,
  103. (PVOID)pwszEntry,
  104. (ULONG)(nLen) * sizeof(WCHAR),
  105. &liOffset,
  106. NULL);
  107. if (!NT_SUCCESS(status)) {
  108. DPFN(eDbgLevelError, "[WriteEntryToLog] 0x%X Failed to make entry", status);
  109. goto exit;
  110. }
  111. exit:
  112. NtClose(hFile);
  113. }
  114. /*++
  115. Creates our log files using the local time.
  116. It goes into %windir%\AppPatch\VLog.
  117. --*/
  118. BOOL
  119. InitializeLogFile(
  120. void
  121. )
  122. {
  123. BOOL fReturn = FALSE;
  124. HANDLE hFile;
  125. SYSTEMTIME st;
  126. UINT nLen = 0;
  127. WCHAR wszFileLog[64];
  128. WCHAR* pwszLogFile = NULL;
  129. WCHAR* pSlash = NULL;
  130. WCHAR* pDot = NULL;
  131. WCHAR wszModPathName[MAX_PATH];
  132. WCHAR wszShortName[MAX_PATH];
  133. WCHAR wszLogHdr[512];
  134. const WCHAR wszLogDir[] = L"\\AppPatch\\VLog";
  135. WCHAR wszUnicodeHdr = 0xFEFF;
  136. UNICODE_STRING strLogFile;
  137. NTSTATUS status;
  138. OBJECT_ATTRIBUTES ObjectAttributes;
  139. IO_STATUS_BLOCK IoStatusBlock;
  140. //
  141. // Format the log header.
  142. //
  143. if (!GetModuleFileName(NULL, wszModPathName, ARRAYSIZE(wszModPathName))) {
  144. wcscpy(wszModPathName, L"unknown");
  145. }
  146. if (_snwprintf(wszLogHdr,
  147. ARRAYSIZE(wszLogHdr) - 1,
  148. L"%lc<?xml version=\"1.0\" encoding=\"UTF-16\"?>\r\n<APPLICATION NAME=\"%ls\">\r\n",
  149. wszUnicodeHdr,
  150. wszModPathName) < 0) {
  151. DPFN(eDbgLevelError, "[InitializeLogFile] Buffer too small (1)");
  152. return FALSE;
  153. }
  154. //
  155. // Get the current time and set up the log file name.
  156. // The format of the log file name is this:
  157. // 'processname_filesys_yyyymmdd_hhmmss.xml'
  158. //
  159. GetLocalTime(&st);
  160. //
  161. // Extract the name of this module without the path and extension.
  162. // Basically we trim off the extension first (if there is one.)
  163. // Next we find out where the directory path ends, then grab
  164. // the file portion and save it away.
  165. //
  166. wszShortName[0] = 0;
  167. pDot = wcsrchr(wszModPathName, '.');
  168. if (pDot) {
  169. *pDot = '\0';
  170. }
  171. pSlash = wcsrchr(wszModPathName, '\\');
  172. if (pSlash) {
  173. wcsncpy(wszShortName, ++pSlash, (wcslen(pSlash) + 1));
  174. }
  175. if (_snwprintf(wszFileLog,
  176. ARRAYSIZE(wszFileLog) - 1,
  177. L"%ls_filesys_%02hu%02hu%02hu_%02hu%02hu%02hu.xml",
  178. wszShortName,
  179. st.wYear,
  180. st.wMonth,
  181. st.wDay,
  182. st.wHour,
  183. st.wMinute,
  184. st.wSecond) < 0) {
  185. DPFN(eDbgLevelError, "[InitializeLogFile] Buffer too small (2)");
  186. return FALSE;
  187. }
  188. //
  189. // Set up the path our log file.
  190. //
  191. nLen = GetSystemWindowsDirectory(NULL, 0);
  192. if (0 == nLen) {
  193. DPFN(eDbgLevelError,
  194. "[InitializeLogFile] %lu Failed to get size for Windows directory path",
  195. GetLastError());
  196. return FALSE;
  197. }
  198. nLen += wcslen(wszFileLog);
  199. nLen += wcslen(wszLogDir);
  200. pwszLogFile = (LPWSTR)MemAlloc((nLen + 2) * sizeof(WCHAR));
  201. if (!pwszLogFile) {
  202. DPFN(eDbgLevelError, "[InitializeLogFile] No memory for log file");
  203. return FALSE;
  204. }
  205. nLen = GetSystemWindowsDirectory(pwszLogFile, (nLen + 2) * sizeof(WCHAR));
  206. if (0 == nLen) {
  207. DPFN(eDbgLevelError,
  208. "[InitializeLogFile] %lu Failed to get Windows directory path",
  209. GetLastError());
  210. return FALSE;
  211. }
  212. wcscat(pwszLogFile, wszLogDir);
  213. //
  214. // Ensure that the %windir%\AppPatch\VLog directory exists.
  215. // If it doesn't, attempt to create it.
  216. //
  217. if (-1 == GetFileAttributes(pwszLogFile)) {
  218. if (!CreateDirectory(pwszLogFile, NULL)) {
  219. DPFN(eDbgLevelError,
  220. "[InitializeLogFile] %lu Failed to create VLog directory",
  221. GetLastError());
  222. return FALSE;
  223. }
  224. }
  225. wcscat(pwszLogFile, L"\\");
  226. wcscat(pwszLogFile, wszFileLog);
  227. //
  228. // Save the pointer as we'll need it later.
  229. //
  230. g_pwszLogFilePath = pwszLogFile;
  231. //
  232. // Attempt to create the new log file.
  233. //
  234. RtlInitUnicodeString(&strLogFile, pwszLogFile);
  235. status = RtlDosPathNameToNtPathName_U(strLogFile.Buffer,
  236. &strLogFile,
  237. NULL,
  238. NULL);
  239. if (!NT_SUCCESS(status)) {
  240. DPFN(eDbgLevelError, "[InitializeLogFile] DOS -> NT failed");
  241. return FALSE;
  242. }
  243. InitializeObjectAttributes(&ObjectAttributes,
  244. &strLogFile,
  245. OBJ_CASE_INSENSITIVE,
  246. NULL,
  247. NULL);
  248. status = NtCreateFile(&hFile,
  249. GENERIC_WRITE | SYNCHRONIZE,
  250. &ObjectAttributes,
  251. &IoStatusBlock,
  252. NULL,
  253. FILE_ATTRIBUTE_NORMAL,
  254. 0,
  255. FILE_CREATE,
  256. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  257. NULL,
  258. 0);
  259. if (!NT_SUCCESS(status)) {
  260. DPFN(eDbgLevelError,
  261. "[InitializeLogFile] 0x%X Failed to create log",
  262. status);
  263. goto cleanup;
  264. }
  265. NtClose(hFile);
  266. //
  267. // Save away the NT path to the file.
  268. //
  269. status = ShimDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE |
  270. RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING,
  271. &strLogFile,
  272. &g_strLogFilePath);
  273. if (!NT_SUCCESS(status)) {
  274. DPFN(eDbgLevelError,
  275. "[InitializeLogFile] Failed to save log file path");
  276. goto cleanup;
  277. }
  278. //
  279. // Write the header to the log.
  280. //
  281. WriteEntryToLog(wszLogHdr);
  282. fReturn = TRUE;
  283. cleanup:
  284. RtlFreeUnicodeString(&strLogFile);
  285. return fReturn;
  286. }
  287. /*++
  288. Displays the name associated with this object.
  289. --*/
  290. void
  291. PrintNameFromHandle(
  292. IN HANDLE hObject
  293. )
  294. {
  295. NTSTATUS status;
  296. WCHAR wszBuffer[MAX_PATH];
  297. OBJECT_NAME_INFORMATION *poni = NULL;
  298. wszBuffer[0] = 0;
  299. poni = (OBJECT_NAME_INFORMATION*)wszBuffer;
  300. status = NtQueryObject(hObject, ObjectNameInformation, poni, MAX_PATH, NULL);
  301. if (NT_SUCCESS(status)) {
  302. DPFN(eDbgLevelInfo, "Handle 0x%08x has name: %ls", hObject, poni->Name.Buffer);
  303. }
  304. }
  305. /*++
  306. Formats the data to form an XML element.
  307. --*/
  308. void
  309. FormatDataIntoElement(
  310. IN OperationType eType,
  311. IN LPCWSTR pwszFilePath
  312. )
  313. {
  314. int nChars = 0;
  315. DWORD dwCount;
  316. DWORD dwAttrCount = 0;
  317. WCHAR wszItem[MAX_PATH];
  318. WCHAR wszOperation[MAX_OPERATION_LENGTH];
  319. PATTRINFO pAttrInfo = NULL;
  320. CString csFilePart(L"");
  321. CString csPathPart(L"");
  322. CString csString(pwszFilePath);
  323. g_wszXMLBuffer[0] = 0;
  324. //
  325. // Replace any & or ' in the file path.
  326. // We have to do this since we're saving to XML.
  327. // Note that the file system doesn't allow < > or "
  328. //
  329. csString.Replace(L"&", L"amp;");
  330. csString.Replace(L"'", L"&apos;");
  331. //
  332. // Put the path into a CString, then break it into pieces
  333. // so we can use it in our element.
  334. //
  335. csString.GetNotLastPathComponent(csPathPart);
  336. csString.GetLastPathComponent(csFilePart);
  337. switch (eType) {
  338. case eCreatedFile:
  339. wcscpy(wszOperation, L"Created File");
  340. break;
  341. case eModifiedFile:
  342. wcscpy(wszOperation, L"Modified File");
  343. break;
  344. case eDeletedFile:
  345. wcscpy(wszOperation, L"Deleted File");
  346. break;
  347. default:
  348. wcscpy(wszOperation, L"Unknown");
  349. break;
  350. }
  351. //
  352. // If we're logging attributes and this is not file deletion, press on.
  353. //
  354. if ((g_dwSettings & LFC_OPTION_ATTRIBUTES) && (eType != eDeletedFile)) {
  355. nChars = wsprintf(g_wszXMLBuffer,
  356. L" <FILE OPERATION=\"%ls\" NAME=\"%ls\" PATH=\"%ls\"",
  357. wszOperation,
  358. csFilePart.Get(),
  359. csPathPart.Get());
  360. //
  361. // Call the attribute manager to get the attributes for this file.
  362. // Loop through all the attributes and add the ones that are available.
  363. //
  364. if (SdbGetFileAttributes(pwszFilePath, &pAttrInfo, &dwAttrCount)) {
  365. for (dwCount = 0; dwCount < dwAttrCount; dwCount++) {
  366. if (pAttrInfo[dwCount].dwFlags & ATTRIBUTE_AVAILABLE) {
  367. if (!SdbFormatAttribute(&pAttrInfo[dwCount], wszItem, MAX_PATH)) {
  368. continue;
  369. }
  370. nChars += wsprintf(g_wszXMLBuffer + nChars, L" %ls", wszItem);
  371. }
  372. }
  373. if (pAttrInfo) {
  374. SdbFreeFileAttributes(pAttrInfo);
  375. }
  376. }
  377. //
  378. // Append the '/>\r\n' to the file element.
  379. //
  380. wsprintf(g_wszXMLBuffer + nChars, L"/>\r\n");
  381. } else {
  382. //
  383. // Format the element without attributes.
  384. //
  385. wsprintf(g_wszXMLBuffer,
  386. L" <FILE OPERATION=\"%ls\" NAME=\"%ls\" PATH=\"%ls\"/>\r\n",
  387. wszOperation,
  388. csFilePart.Get(),
  389. csPathPart.Get());
  390. }
  391. WriteEntryToLog(g_wszXMLBuffer);
  392. }
  393. /*++
  394. Format file system data passed in and write it to the log.
  395. --*/
  396. void
  397. FormatFileDataLogEntry(
  398. IN PLOG_HANDLE pHandle
  399. )
  400. {
  401. //
  402. // Ensure that our parameters are valid before going any further.
  403. //
  404. if (!pHandle || !pHandle->pwszFilePath) {
  405. DPFN(eDbgLevelError, "[FormatFileDataLogEntry] Invalid parameter(s)");
  406. return;
  407. }
  408. //
  409. // Save ourselves a lot of work by logging only what needs to be logged.
  410. //
  411. if ((pHandle->dwFlags & LFC_EXISTING) &&
  412. (!(pHandle->dwFlags & LFC_DELETED)) &&
  413. (!(pHandle->dwFlags & LFC_MODIFIED))) {
  414. return;
  415. }
  416. //
  417. // Check for an unapproved file write, and keep moving afterward.
  418. //
  419. if (pHandle->dwFlags & LFC_UNAPPRVFW) {
  420. VLOG(VLOG_LEVEL_ERROR,
  421. VLOG_LOGFILECHANGES_UFW,
  422. "Path and Filename: %ls",
  423. pHandle->pwszFilePath);
  424. }
  425. //
  426. // Move through the different operations.
  427. //
  428. // 1. Check for a deletion of an existing file.
  429. //
  430. if ((pHandle->dwFlags & LFC_DELETED) &&
  431. (pHandle->dwFlags & LFC_EXISTING)) {
  432. FormatDataIntoElement(eDeletedFile, pHandle->pwszFilePath);
  433. return;
  434. }
  435. //
  436. // 2. Check for modification of an existing file.
  437. //
  438. if ((pHandle->dwFlags & LFC_MODIFIED) &&
  439. (pHandle->dwFlags & LFC_EXISTING)) {
  440. FormatDataIntoElement(eModifiedFile, pHandle->pwszFilePath);
  441. return;
  442. }
  443. //
  444. // 3. Check for creation of a new file.
  445. //
  446. if (!(pHandle->dwFlags & LFC_EXISTING) &&
  447. (!(pHandle->dwFlags & LFC_DELETED))) {
  448. FormatDataIntoElement(eCreatedFile, pHandle->pwszFilePath);
  449. return;
  450. }
  451. }
  452. /*++
  453. Writes the closing element to the file and outputs the log file location.
  454. --*/
  455. void
  456. CloseLogFile(
  457. void
  458. )
  459. {
  460. WCHAR wszBuffer[] = L"</APPLICATION>";
  461. WriteEntryToLog(wszBuffer);
  462. VLOG(VLOG_LEVEL_INFO, VLOG_LOGFILECHANGES_LOGLOC, "%ls", g_pwszLogFilePath);
  463. }
  464. /*++
  465. Write the entire linked list out to the log file.
  466. --*/
  467. BOOL
  468. WriteListToLogFile(
  469. void
  470. )
  471. {
  472. PLIST_ENTRY pCurrent = NULL;
  473. PLOG_HANDLE pHandle = NULL;
  474. //
  475. // Walk the list and write each node to the log file.
  476. //
  477. pCurrent = g_OpenHandleListHead.Blink;
  478. while (pCurrent != &g_OpenHandleListHead) {
  479. pHandle = CONTAINING_RECORD(pCurrent, LOG_HANDLE, Entry);
  480. FormatFileDataLogEntry(pHandle);
  481. pCurrent = pCurrent->Blink;
  482. }
  483. CloseLogFile();
  484. return TRUE;
  485. }
  486. /*++
  487. Given a file path, attempt to locate it in the list.
  488. This function may not always return a pointer.
  489. --*/
  490. PLOG_HANDLE
  491. FindPathInList(
  492. IN LPCWSTR pwszFilePath
  493. )
  494. {
  495. BOOL fFound = FALSE;
  496. PLIST_ENTRY pCurrent = NULL;
  497. PLOG_HANDLE pHandle = NULL;
  498. //
  499. // Attempt to locate the entry in the list.
  500. //
  501. pCurrent = g_OpenHandleListHead.Flink;
  502. while (pCurrent != &g_OpenHandleListHead) {
  503. pHandle = CONTAINING_RECORD(pCurrent, LOG_HANDLE, Entry);
  504. if (!_wcsicmp(pwszFilePath, pHandle->pwszFilePath)) {
  505. fFound = TRUE;
  506. break;
  507. }
  508. pCurrent = pCurrent->Flink;
  509. }
  510. return (fFound ? pHandle : NULL);
  511. }
  512. /*++
  513. Given a file handle and a file path, add an entry to the list.
  514. --*/
  515. PLOG_HANDLE
  516. AddFileToList(
  517. IN HANDLE hFile,
  518. IN LPCWSTR pwszPath,
  519. IN BOOL fExisting,
  520. IN ULONG ulCreateOptions
  521. )
  522. {
  523. PLOG_HANDLE pHandle = NULL;
  524. if (!pwszPath) {
  525. DPFN(eDbgLevelError, "[AddFileToList] Invalid parameter");
  526. return NULL;
  527. }
  528. pHandle = (PLOG_HANDLE)MemAlloc(sizeof(LOG_HANDLE));
  529. if (!pHandle) {
  530. DPFN(eDbgLevelError, "[AddFileToList] Failed to allocate mem for struct");
  531. return NULL;
  532. }
  533. pHandle->pwszFilePath = (LPWSTR)MemAlloc((wcslen(pwszPath) + 1) * sizeof(WCHAR));
  534. if (!pHandle->pwszFilePath) {
  535. DPFN(eDbgLevelError, "[AddFileToList] Failed to allocate mem for path");
  536. MemFree(pHandle);
  537. return NULL;
  538. }
  539. if ((ulCreateOptions == FILE_OVERWRITE_IF) && fExisting) {
  540. pHandle->dwFlags |= LFC_MODIFIED;
  541. }
  542. if (ulCreateOptions & FILE_DELETE_ON_CLOSE) {
  543. pHandle->dwFlags |= LFC_DELETED;
  544. }
  545. pHandle->cHandles = 1;
  546. pHandle->hFile[0] = hFile ? hFile : INVALID_HANDLE_VALUE;
  547. if (fExisting) {
  548. pHandle->dwFlags |= LFC_EXISTING;
  549. }
  550. wcscpy(pHandle->pwszFilePath, pwszPath);
  551. DPFN(eDbgLevelInfo, "[AddFileToList] Adding entry: %p", pHandle);
  552. InsertHeadList(&g_OpenHandleListHead, &pHandle->Entry);
  553. return pHandle;
  554. }
  555. /*++
  556. Given a file handle, return a pointer to an entry in the list.
  557. This function should always return a pointer. If not, fire a breakpoint.
  558. --*/
  559. PLOG_HANDLE
  560. FindHandleInArray(
  561. IN HANDLE hFile
  562. )
  563. {
  564. UINT uCount;
  565. BOOL fFound = FALSE;
  566. PLIST_ENTRY pCurrent = NULL;
  567. PLOG_HANDLE pFindHandle = NULL;
  568. //
  569. // An invalid handle value is useless.
  570. //
  571. if (INVALID_HANDLE_VALUE == hFile) {
  572. DPFN(eDbgLevelError, "[FindHandleInArray] Invalid handle passed!");
  573. return FALSE;
  574. }
  575. pCurrent = g_OpenHandleListHead.Flink;
  576. while (pCurrent != &g_OpenHandleListHead) {
  577. pFindHandle = CONTAINING_RECORD(pCurrent, LOG_HANDLE, Entry);
  578. //
  579. // Step through this guy's array looking for the handle.
  580. //
  581. for (uCount = 0; uCount < pFindHandle->cHandles; uCount++) {
  582. if (pFindHandle->hFile[uCount] == hFile) {
  583. fFound = TRUE;
  584. break;
  585. }
  586. }
  587. if (fFound) {
  588. break;
  589. }
  590. pCurrent = pCurrent->Flink;
  591. }
  592. //
  593. // If the handle was not found, send output to the debugger.
  594. //
  595. if (!fFound) {
  596. DPFN(eDbgLevelError,
  597. "[FindHandleInArray] Handle 0x%08x not found!",
  598. hFile);
  599. PrintNameFromHandle(hFile);
  600. }
  601. return (pFindHandle ? pFindHandle : NULL);
  602. }
  603. /*++
  604. Given a file handle, remove it from the array in the list.
  605. --*/
  606. BOOL
  607. RemoveHandleFromArray(
  608. IN HANDLE hFile
  609. )
  610. {
  611. UINT uCount;
  612. PLIST_ENTRY pCurrent = NULL;
  613. PLOG_HANDLE pFindHandle = NULL;
  614. //
  615. // An invalid handle value is useless.
  616. //
  617. if (INVALID_HANDLE_VALUE == hFile) {
  618. DPFN(eDbgLevelError, "[RemoveHandleFromArray] Invalid handle passed!");
  619. return FALSE;
  620. }
  621. pCurrent = g_OpenHandleListHead.Flink;
  622. while (pCurrent != &g_OpenHandleListHead) {
  623. pFindHandle = CONTAINING_RECORD(pCurrent, LOG_HANDLE, Entry);
  624. //
  625. // Step through this guy's array looking for the handle.
  626. //
  627. for (uCount = 0; uCount < pFindHandle->cHandles; uCount++) {
  628. //
  629. // If we find the handle, set the array element to -1 and
  630. // decrement the count of handles for this entry.
  631. //
  632. if (pFindHandle->hFile[uCount] == hFile) {
  633. DPFN(eDbgLevelInfo,
  634. "[RemoveHandleFromArray] Removing handle 0x%08x",
  635. hFile);
  636. pFindHandle->hFile[uCount] = INVALID_HANDLE_VALUE;
  637. pFindHandle->cHandles--;
  638. return TRUE;
  639. }
  640. }
  641. pCurrent = pCurrent->Flink;
  642. }
  643. return TRUE;
  644. }
  645. /*++
  646. Determine if the application is performing an operation in Windows or Program Files.
  647. --*/
  648. void
  649. CheckForUnapprovedFileWrite(
  650. IN PLOG_HANDLE pHandle
  651. )
  652. {
  653. int nPosition;
  654. //
  655. // Check our flags and search for the directories accordingly.
  656. // If we find a match, we're done.
  657. //
  658. CString csPath(pHandle->pwszFilePath);
  659. csPath.MakeLower();
  660. if (g_dwSettings & LFC_OPTION_UFW_WINDOWS) {
  661. nPosition = 0;
  662. nPosition = csPath.Find(L":\\windows");
  663. if (nPosition != -1) {
  664. pHandle->dwFlags |= LFC_UNAPPRVFW;
  665. return;
  666. }
  667. }
  668. if (g_dwSettings & LFC_OPTION_UFW_PROGFILES) {
  669. nPosition = 0;
  670. nPosition = csPath.Find(L":\\program files");
  671. if (nPosition != -1) {
  672. pHandle->dwFlags |= LFC_UNAPPRVFW;
  673. return;
  674. }
  675. }
  676. }
  677. /*++
  678. Inserts a handle into an existing list entry.
  679. --*/
  680. void
  681. InsertHandleIntoList(
  682. IN HANDLE hFile,
  683. IN PLOG_HANDLE pHandle
  684. )
  685. {
  686. UINT uCount = 0;
  687. //
  688. // Insert the handle into an empty spot and
  689. // update the number of handles we're storing.
  690. // Make sure we don't overstep the array bounds.
  691. //
  692. for (uCount = 0; uCount < pHandle->cHandles; uCount++) {
  693. if (INVALID_HANDLE_VALUE == pHandle->hFile[uCount]) {
  694. break;
  695. }
  696. }
  697. if (uCount >= MAX_NUM_HANDLES) {
  698. DPFN(eDbgLevelError, "[InsertHandleIntoList] Handle count reached");
  699. return;
  700. }
  701. pHandle->hFile[uCount] = hFile;
  702. pHandle->cHandles++;
  703. //
  704. // It's not possible to get a handle to a file that's been deleted,
  705. // so remove these bits.
  706. //
  707. pHandle->dwFlags &= ~LFC_DELETED;
  708. }
  709. /*++
  710. Does all the work of updating the linked list.
  711. --*/
  712. void
  713. UpdateFileList(
  714. IN OperationType eType,
  715. IN LPWSTR pwszFilePath,
  716. IN HANDLE hFile,
  717. IN ULONG ulCreateDisposition,
  718. IN BOOL fExisting
  719. )
  720. {
  721. UINT uCount;
  722. DWORD dwLen = 0;
  723. PLOG_HANDLE pHandle = NULL;
  724. switch (eType) {
  725. case eCreatedFile:
  726. case eOpenedFile:
  727. //
  728. // Attempt to find the path in the list.
  729. // We need to check the CreateFile flags as they could
  730. // change an existing file.
  731. //
  732. pHandle = FindPathInList(pwszFilePath);
  733. if (pHandle) {
  734. //
  735. // If the file was created with the CREATE_ALWAYS flag,
  736. // and the file was an existing one, mark it changed.
  737. //
  738. if ((ulCreateDisposition == FILE_OVERWRITE_IF) && fExisting) {
  739. pHandle->dwFlags |= LFC_MODIFIED;
  740. if ((g_dwSettings & LFC_OPTION_UFW_WINDOWS) ||
  741. (g_dwSettings & LFC_OPTION_UFW_PROGFILES)) {
  742. CheckForUnapprovedFileWrite(pHandle);
  743. }
  744. }
  745. //
  746. // If the file was opened with the FILE_DELETE_ON_CLOSE flag,
  747. // mark it deleted.
  748. //
  749. if (ulCreateDisposition & FILE_DELETE_ON_CLOSE) {
  750. pHandle->dwFlags |= LFC_DELETED;
  751. }
  752. InsertHandleIntoList(hFile, pHandle);
  753. break;
  754. }
  755. //
  756. // The file path was not in the list, so we've never seen
  757. // this file before. We're going to add this guy to the list.
  758. //
  759. AddFileToList(hFile, pwszFilePath, fExisting, ulCreateDisposition);
  760. break;
  761. case eModifiedFile:
  762. //
  763. // No file path is available, so find the handle in the list.
  764. //
  765. pHandle = FindHandleInArray(hFile);
  766. if (pHandle) {
  767. pHandle->dwFlags |= LFC_MODIFIED;
  768. if ((g_dwSettings & LFC_OPTION_UFW_WINDOWS) ||
  769. (g_dwSettings & LFC_OPTION_UFW_PROGFILES)) {
  770. CheckForUnapprovedFileWrite(pHandle);
  771. }
  772. }
  773. break;
  774. case eDeletedFile:
  775. //
  776. // Deletetion comes from two places. One provides a file path,
  777. // the other a handle. Determine which one we have.
  778. //
  779. if (hFile) {
  780. pHandle = FindHandleInArray(hFile);
  781. } else {
  782. pHandle = FindPathInList(pwszFilePath);
  783. }
  784. //
  785. // Rare case: If a handle wasn't available, deletion
  786. // is coming from NtDeleteFile, which hardly ever
  787. // gets called directly. Add the file path to the list
  788. // so we can track this deletion.
  789. //
  790. if (!pHandle && !hFile) {
  791. pHandle = AddFileToList(NULL, pwszFilePath, TRUE, 0);
  792. }
  793. if (pHandle) {
  794. pHandle->dwFlags |= LFC_DELETED;
  795. if ((g_dwSettings & LFC_OPTION_UFW_WINDOWS) ||
  796. (g_dwSettings & LFC_OPTION_UFW_PROGFILES)) {
  797. CheckForUnapprovedFileWrite(pHandle);
  798. }
  799. }
  800. break;
  801. case eRenamedFile:
  802. {
  803. PLOG_HANDLE pSrcHandle = NULL;
  804. PLOG_HANDLE pDestHandle = NULL;
  805. WCHAR wszFullPath[MAX_PATH * 2];
  806. WCHAR* pSlash = NULL;
  807. UINT cbCopy;
  808. //
  809. // A rename is two separate operations in one.
  810. // * Delete of an existing file.
  811. // * Create of a new file.
  812. //
  813. // In this case, we attempt to find the destination file
  814. // in our list. If the file is not there, we add to the
  815. // list, then mark it as modified.
  816. //
  817. // As far as the source file, we mark it as deleted since it's
  818. // gone from the disk after the rename.
  819. //
  820. pSrcHandle = FindHandleInArray(hFile);
  821. if (pSrcHandle) {
  822. pDestHandle = FindPathInList(pwszFilePath);
  823. if (!pDestHandle) {
  824. //
  825. // The rename will only contain the new file name,
  826. // not the path. Build a full path to the new file
  827. // prior to adding it to the list.
  828. //
  829. wcsncpy(wszFullPath,
  830. pSrcHandle->pwszFilePath,
  831. wcslen(pSrcHandle->pwszFilePath) + 1);
  832. pSlash = wcsrchr(wszFullPath, '\\');
  833. if (pSlash) {
  834. *++pSlash = '\0';
  835. }
  836. wcscat(wszFullPath, pwszFilePath);
  837. pDestHandle = AddFileToList((HANDLE)-1,
  838. wszFullPath,
  839. fExisting,
  840. ulCreateDisposition);
  841. }
  842. if (pDestHandle) {
  843. pDestHandle->dwFlags = 0;
  844. pDestHandle->dwFlags |= LFC_MODIFIED;
  845. if ((g_dwSettings & LFC_OPTION_UFW_WINDOWS) ||
  846. (g_dwSettings & LFC_OPTION_UFW_PROGFILES)) {
  847. CheckForUnapprovedFileWrite(pDestHandle);
  848. }
  849. }
  850. pSrcHandle->dwFlags &= ~LFC_MODIFIED;
  851. pSrcHandle->dwFlags |= LFC_DELETED;
  852. if ((g_dwSettings & LFC_OPTION_UFW_WINDOWS) ||
  853. (g_dwSettings & LFC_OPTION_UFW_PROGFILES)) {
  854. CheckForUnapprovedFileWrite(pSrcHandle);
  855. }
  856. }
  857. break;
  858. }
  859. default:
  860. DPFN(eDbgLevelError, "[UpdateFileList] Invalid enum type!");
  861. return;
  862. }
  863. }
  864. /*++
  865. Given an NT path, convert it to a DOS path.
  866. --*/
  867. BOOL
  868. ConvertNtPathToDosPath(
  869. IN PUNICODE_STRING pstrSource,
  870. IN OUT PRTL_UNICODE_STRING_BUFFER pstrDest
  871. )
  872. {
  873. NTSTATUS status;
  874. if (!pstrSource || !pstrDest) {
  875. DPFN(eDbgLevelError, "[ConvertNtPathToDosPath] Invalid parameter(s)");
  876. return FALSE;
  877. }
  878. status = ShimAssignUnicodeStringBuffer(pstrDest, pstrSource);
  879. if (!NT_SUCCESS(status)) {
  880. DPFN(eDbgLevelError, "[ConvertNtPathToDosPath] Failed to initialize DOS path buffer");
  881. return FALSE;
  882. }
  883. status = ShimNtPathNameToDosPathName(0, pstrDest, 0, NULL);
  884. if (!NT_SUCCESS(status)) {
  885. DPFN(eDbgLevelError, "[ConvertNtPathToDosPath] Failed to convert NT -> DOS path");
  886. return FALSE;
  887. }
  888. return TRUE;
  889. }
  890. NTSTATUS
  891. APIHOOK(NtCreateFile)(
  892. OUT PHANDLE FileHandle,
  893. IN ACCESS_MASK DesiredAccess,
  894. IN POBJECT_ATTRIBUTES ObjectAttributes,
  895. OUT PIO_STATUS_BLOCK IoStatusBlock,
  896. IN PLARGE_INTEGER AllocationSize OPTIONAL,
  897. IN ULONG FileAttributes,
  898. IN ULONG ShareAccess,
  899. IN ULONG CreateDisposition,
  900. IN ULONG CreateOptions,
  901. IN PVOID EaBuffer OPTIONAL,
  902. IN ULONG EaLength
  903. )
  904. {
  905. RTL_UNICODE_STRING_BUFFER DosPathBuffer;
  906. UCHAR PathBuffer[MAX_PATH * 2];
  907. NTSTATUS status;
  908. BOOL fExists = FALSE;
  909. BOOL fConverted = FALSE;
  910. CLock cLock;
  911. RtlInitUnicodeStringBuffer(&DosPathBuffer, PathBuffer, sizeof(PathBuffer));
  912. fConverted = ConvertNtPathToDosPath(ObjectAttributes->ObjectName, &DosPathBuffer);
  913. if (!fConverted) {
  914. DPFN(eDbgLevelError,
  915. "[NtCreateFile] Failed to convert NT path: %ls",
  916. ObjectAttributes->ObjectName->Buffer);
  917. }
  918. fExists = RtlDoesFileExists_U(DosPathBuffer.String.Buffer);
  919. status = ORIGINAL_API(NtCreateFile)(FileHandle,
  920. DesiredAccess,
  921. ObjectAttributes,
  922. IoStatusBlock,
  923. AllocationSize,
  924. FileAttributes,
  925. ShareAccess,
  926. CreateDisposition,
  927. CreateOptions,
  928. EaBuffer,
  929. EaLength);
  930. //
  931. // Three conditions are required before the file is added to the list.
  932. // 1. The file must be a file system object. RtlDoesFileExists_U will
  933. // return FALSE if it's not.
  934. //
  935. // 2. We must have been able to convert the NT path to a DOS path.
  936. //
  937. // 3. The call to NtCreateFile must have succeeded.
  938. //
  939. if (RtlDoesFileExists_U(DosPathBuffer.String.Buffer) && fConverted && NT_SUCCESS(status)) {
  940. UpdateFileList(eCreatedFile,
  941. DosPathBuffer.String.Buffer,
  942. *FileHandle,
  943. CreateDisposition,
  944. fExists);
  945. }
  946. RtlFreeUnicodeStringBuffer(&DosPathBuffer);
  947. return status;
  948. }
  949. NTSTATUS
  950. APIHOOK(NtOpenFile)(
  951. OUT PHANDLE FileHandle,
  952. IN ACCESS_MASK DesiredAccess,
  953. IN POBJECT_ATTRIBUTES ObjectAttributes,
  954. OUT PIO_STATUS_BLOCK IoStatusBlock,
  955. IN ULONG ShareAccess,
  956. IN ULONG OpenOptions
  957. )
  958. {
  959. RTL_UNICODE_STRING_BUFFER DosPathBuffer;
  960. UCHAR PathBuffer[MAX_PATH * 2];
  961. NTSTATUS status;
  962. BOOL fConverted = FALSE;
  963. CLock cLock;
  964. RtlInitUnicodeStringBuffer(&DosPathBuffer, PathBuffer, sizeof(PathBuffer));
  965. fConverted = ConvertNtPathToDosPath(ObjectAttributes->ObjectName, &DosPathBuffer);
  966. if (!fConverted) {
  967. DPFN(eDbgLevelError,
  968. "[NtOpenFile] Failed to convert NT path: %ls",
  969. ObjectAttributes->ObjectName->Buffer);
  970. }
  971. status = ORIGINAL_API(NtOpenFile)(FileHandle,
  972. DesiredAccess,
  973. ObjectAttributes,
  974. IoStatusBlock,
  975. ShareAccess,
  976. OpenOptions);
  977. //
  978. // Two conditions are required before we add this handle to the list.
  979. // 1. We must have been able to convert the NT path to a DOS path.
  980. //
  981. // 2. The call to NtOpenFile must have succeeded.
  982. //
  983. if (fConverted && NT_SUCCESS(status)) {
  984. UpdateFileList(eOpenedFile,
  985. DosPathBuffer.String.Buffer,
  986. *FileHandle,
  987. OpenOptions,
  988. TRUE);
  989. }
  990. RtlFreeUnicodeStringBuffer(&DosPathBuffer);
  991. return status;
  992. }
  993. NTSTATUS
  994. APIHOOK(NtClose)(
  995. IN HANDLE Handle
  996. )
  997. {
  998. CLock cLock;
  999. RemoveHandleFromArray(Handle);
  1000. return ORIGINAL_API(NtClose)(Handle);
  1001. }
  1002. NTSTATUS
  1003. APIHOOK(NtWriteFile)(
  1004. IN HANDLE FileHandle,
  1005. IN HANDLE Event OPTIONAL,
  1006. IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
  1007. IN PVOID ApcContext OPTIONAL,
  1008. OUT PIO_STATUS_BLOCK IoStatusBlock,
  1009. IN PVOID Buffer,
  1010. IN ULONG Length,
  1011. IN PLARGE_INTEGER ByteOffset OPTIONAL,
  1012. IN PULONG Key OPTIONAL
  1013. )
  1014. {
  1015. NTSTATUS status;
  1016. CLock cLock;
  1017. status = ORIGINAL_API(NtWriteFile)(FileHandle,
  1018. Event,
  1019. ApcRoutine,
  1020. ApcContext,
  1021. IoStatusBlock,
  1022. Buffer,
  1023. Length,
  1024. ByteOffset,
  1025. Key);
  1026. //
  1027. // Handle the case in which the caller is using overlapped I/O.
  1028. //
  1029. if (STATUS_PENDING == status) {
  1030. status = NtWaitForSingleObject(Event, FALSE, NULL);
  1031. }
  1032. //
  1033. // If the call to NtWriteFile succeeded, update the list.
  1034. //
  1035. if (NT_SUCCESS(status)) {
  1036. UpdateFileList(eModifiedFile,
  1037. NULL,
  1038. FileHandle,
  1039. 0,
  1040. TRUE);
  1041. }
  1042. return status;
  1043. }
  1044. NTSTATUS
  1045. APIHOOK(NtWriteFileGather)(
  1046. IN HANDLE FileHandle,
  1047. IN HANDLE Event OPTIONAL,
  1048. IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
  1049. IN PVOID ApcContext OPTIONAL,
  1050. OUT PIO_STATUS_BLOCK IoStatusBlock,
  1051. IN PFILE_SEGMENT_ELEMENT SegmentArray,
  1052. IN ULONG Length,
  1053. IN PLARGE_INTEGER ByteOffset OPTIONAL,
  1054. IN PULONG Key OPTIONAL
  1055. )
  1056. {
  1057. NTSTATUS status;
  1058. CLock cLock;
  1059. status = ORIGINAL_API(NtWriteFileGather)(FileHandle,
  1060. Event,
  1061. ApcRoutine,
  1062. ApcContext,
  1063. IoStatusBlock,
  1064. SegmentArray,
  1065. Length,
  1066. ByteOffset,
  1067. Key);
  1068. //
  1069. // Handle the case in which the caller is using overlapped I/O.
  1070. //
  1071. if (STATUS_PENDING == status) {
  1072. status = NtWaitForSingleObject(Event, FALSE, NULL);
  1073. }
  1074. //
  1075. // If the call to NtWriteFileGather succeeded, update the list.
  1076. //
  1077. if (NT_SUCCESS(status)) {
  1078. UpdateFileList(eModifiedFile,
  1079. NULL,
  1080. FileHandle,
  1081. 0,
  1082. TRUE);
  1083. }
  1084. return status;
  1085. }
  1086. NTSTATUS
  1087. APIHOOK(NtSetInformationFile)(
  1088. IN HANDLE FileHandle,
  1089. OUT PIO_STATUS_BLOCK IoStatusBlock,
  1090. IN PVOID FileInformation,
  1091. IN ULONG Length,
  1092. IN FILE_INFORMATION_CLASS FileInformationClass
  1093. )
  1094. {
  1095. NTSTATUS status;
  1096. CLock cLock;
  1097. status = ORIGINAL_API(NtSetInformationFile)(FileHandle,
  1098. IoStatusBlock,
  1099. FileInformation,
  1100. Length,
  1101. FileInformationClass);
  1102. //
  1103. // This API is called for a variety of reasons, but were only
  1104. // interested in a couple different cases.
  1105. //
  1106. if (NT_SUCCESS(status)) {
  1107. switch (FileInformationClass) {
  1108. case FileAllocationInformation:
  1109. case FileEndOfFileInformation:
  1110. UpdateFileList(eModifiedFile,
  1111. NULL,
  1112. FileHandle,
  1113. 0,
  1114. TRUE);
  1115. break;
  1116. case FileRenameInformation:
  1117. {
  1118. PFILE_RENAME_INFORMATION pRenameInfo = NULL;
  1119. UNICODE_STRING ustrTemp;
  1120. RTL_UNICODE_STRING_BUFFER ubufDosPath;
  1121. WCHAR* pwszPathBuffer = NULL;
  1122. WCHAR* pwszTempBuffer = NULL;
  1123. DWORD dwPathBufSize = 0;
  1124. pRenameInfo = (PFILE_RENAME_INFORMATION)FileInformation;
  1125. pwszTempBuffer = (WCHAR*)MemAlloc(pRenameInfo->FileNameLength + sizeof(WCHAR));
  1126. //
  1127. // allow for possible expansion when converting to DOS path
  1128. //
  1129. dwPathBufSize = pRenameInfo->FileNameLength + MAX_PATH;
  1130. pwszPathBuffer = (WCHAR*)MemAlloc(dwPathBufSize);
  1131. if (!pwszTempBuffer || !pwszPathBuffer) {
  1132. goto outRename;
  1133. }
  1134. //
  1135. // copy the string into a local buffer and terminate it.
  1136. //
  1137. memcpy(pwszTempBuffer, pRenameInfo->FileName, pRenameInfo->FileNameLength);
  1138. pwszTempBuffer[pRenameInfo->FileNameLength / 2] = 0;
  1139. RtlInitUnicodeString(&ustrTemp, pwszTempBuffer);
  1140. RtlInitUnicodeStringBuffer(&ubufDosPath, (PUCHAR)pwszPathBuffer, dwPathBufSize);
  1141. //
  1142. // Convert the path from DOS to NT, and if successful,
  1143. // update the list.
  1144. //
  1145. if (!ConvertNtPathToDosPath(&ustrTemp, &ubufDosPath)) {
  1146. DPFN(eDbgLevelError,
  1147. "[NtSetInformationFile] Failed to convert NT path: %ls",
  1148. pRenameInfo->FileName);
  1149. } else {
  1150. UpdateFileList(eRenamedFile,
  1151. ubufDosPath.String.Buffer,
  1152. FileHandle,
  1153. 0,
  1154. TRUE);
  1155. }
  1156. outRename:
  1157. if (pwszTempBuffer) {
  1158. MemFree(pwszTempBuffer);
  1159. }
  1160. if (pwszPathBuffer) {
  1161. MemFree(pwszPathBuffer);
  1162. }
  1163. break;
  1164. }
  1165. case FileDispositionInformation:
  1166. {
  1167. PFILE_DISPOSITION_INFORMATION pDisposition = NULL;
  1168. pDisposition = (PFILE_DISPOSITION_INFORMATION)FileInformation;
  1169. //
  1170. // Determine if the file is being deleted.
  1171. // Note that we have to undefine DeleteFile.
  1172. //
  1173. #undef DeleteFile
  1174. if (pDisposition) {
  1175. if (pDisposition->DeleteFile) {
  1176. UpdateFileList(eDeletedFile,
  1177. NULL,
  1178. FileHandle,
  1179. 0,
  1180. TRUE);
  1181. }
  1182. }
  1183. break;
  1184. }
  1185. }
  1186. }
  1187. return status;
  1188. }
  1189. NTSTATUS
  1190. APIHOOK(NtDeleteFile)(
  1191. IN POBJECT_ATTRIBUTES ObjectAttributes
  1192. )
  1193. {
  1194. RTL_UNICODE_STRING_BUFFER DosPathBuffer;
  1195. UCHAR PathBuffer[MAX_PATH * 2];
  1196. NTSTATUS status;
  1197. BOOL fConverted = FALSE;
  1198. CLock cLock;
  1199. RtlInitUnicodeStringBuffer(&DosPathBuffer, PathBuffer, sizeof(PathBuffer));
  1200. fConverted = ConvertNtPathToDosPath(ObjectAttributes->ObjectName, &DosPathBuffer);
  1201. if (!fConverted) {
  1202. DPFN(eDbgLevelError,
  1203. "[NtDeleteFile] Failed to convert NT path: %ls",
  1204. ObjectAttributes->ObjectName->Buffer);
  1205. }
  1206. status = ORIGINAL_API(NtDeleteFile)(ObjectAttributes);
  1207. if (NT_SUCCESS(status)) {
  1208. UpdateFileList(eDeletedFile,
  1209. DosPathBuffer.String.Buffer,
  1210. NULL,
  1211. 0,
  1212. TRUE);
  1213. }
  1214. return status;
  1215. }
  1216. /*++
  1217. Controls our property page that is displayed in the Verifer.
  1218. --*/
  1219. LRESULT CALLBACK
  1220. DlgOptions(
  1221. HWND hDlg,
  1222. UINT message,
  1223. WPARAM wParam,
  1224. LPARAM lParam
  1225. )
  1226. {
  1227. static LPCWSTR szExeName;
  1228. switch (message) {
  1229. case WM_INITDIALOG:
  1230. //
  1231. // find out what exe we're handling settings for
  1232. //
  1233. szExeName = ExeNameFromLParam(lParam);
  1234. g_dwSettings = GetShimSettingDWORD(L"LogFileChanges", szExeName, L"LogSettings", 1);
  1235. if (g_dwSettings & LFC_OPTION_ATTRIBUTES) {
  1236. CheckDlgButton(hDlg, IDC_LFC_LOG_ATTRIBUTES, BST_CHECKED);
  1237. }
  1238. if (g_dwSettings & LFC_OPTION_UFW_WINDOWS) {
  1239. CheckDlgButton(hDlg, IDC_LFC_UFW_WINDOWS, BST_CHECKED);
  1240. }
  1241. if (g_dwSettings & LFC_OPTION_UFW_PROGFILES) {
  1242. CheckDlgButton(hDlg, IDC_LFC_UFW_PROGFILES, BST_CHECKED);
  1243. }
  1244. return TRUE;
  1245. case WM_COMMAND:
  1246. switch (LOWORD(wParam)) {
  1247. case IDC_LFC_BTN_DEFAULT:
  1248. g_dwSettings = 0;
  1249. g_dwSettings = LFC_OPTION_ATTRIBUTES;
  1250. CheckDlgButton(hDlg, IDC_LFC_LOG_ATTRIBUTES, BST_CHECKED);
  1251. CheckDlgButton(hDlg, IDC_LFC_UFW_WINDOWS, BST_UNCHECKED);
  1252. CheckDlgButton(hDlg, IDC_LFC_UFW_PROGFILES, BST_UNCHECKED);
  1253. break;
  1254. }
  1255. break;
  1256. case WM_NOTIFY:
  1257. switch (((NMHDR FAR *) lParam)->code) {
  1258. case PSN_APPLY:
  1259. {
  1260. UINT uState;
  1261. g_dwSettings = 0;
  1262. uState = IsDlgButtonChecked(hDlg, IDC_LFC_LOG_ATTRIBUTES);
  1263. if (BST_CHECKED == uState) {
  1264. g_dwSettings = LFC_OPTION_ATTRIBUTES;
  1265. }
  1266. uState = IsDlgButtonChecked(hDlg, IDC_LFC_UFW_WINDOWS);
  1267. if (BST_CHECKED == uState) {
  1268. g_dwSettings |= LFC_OPTION_UFW_WINDOWS;
  1269. }
  1270. uState = IsDlgButtonChecked(hDlg, IDC_LFC_UFW_PROGFILES);
  1271. if (BST_CHECKED == uState) {
  1272. g_dwSettings |= LFC_OPTION_UFW_PROGFILES;
  1273. }
  1274. SaveShimSettingDWORD(L"LogFileChanges", szExeName, L"LogSettings", g_dwSettings);
  1275. }
  1276. break;
  1277. }
  1278. break;
  1279. }
  1280. return FALSE;
  1281. }
  1282. /*++
  1283. Initialize the list head and the log file.
  1284. --*/
  1285. BOOL
  1286. InitializeShim(
  1287. void
  1288. )
  1289. {
  1290. //
  1291. // Initialize our list head.
  1292. //
  1293. InitializeListHead(&g_OpenHandleListHead);
  1294. //
  1295. // Get our settings and store them.
  1296. //
  1297. WCHAR szExe[100];
  1298. GetCurrentExeName(szExe, 100);
  1299. g_dwSettings = GetShimSettingDWORD(L"LogFileChanges", szExe, L"LogSettings", 1);
  1300. //
  1301. // Initialize our log file.
  1302. //
  1303. return InitializeLogFile();
  1304. }
  1305. /*++
  1306. Handle process attach/detach notifications.
  1307. --*/
  1308. BOOL
  1309. NOTIFY_FUNCTION(
  1310. DWORD fdwReason
  1311. )
  1312. {
  1313. if (fdwReason == DLL_PROCESS_ATTACH) {
  1314. return InitializeShim();
  1315. } else if (fdwReason == DLL_PROCESS_DETACH) {
  1316. return WriteListToLogFile();
  1317. }
  1318. return TRUE;
  1319. }
  1320. SHIM_INFO_BEGIN()
  1321. SHIM_INFO_DESCRIPTION(AVS_LOGFILECHANGES_DESC)
  1322. SHIM_INFO_FRIENDLY_NAME(AVS_LOGFILECHANGES_FRIENDLY)
  1323. SHIM_INFO_VERSION(1, 6)
  1324. SHIM_INFO_INCLUDE_EXCLUDE("I:kernel32.dll E:rpcrt4.dll ntdll.dll")
  1325. SHIM_INFO_OPTIONS_PAGE(IDD_LOGFILECHANGES_OPTIONS, DlgOptions)
  1326. SHIM_INFO_FLAGS(AVRF_FLAG_NO_DEFAULT)
  1327. SHIM_INFO_END()
  1328. /*++
  1329. Register hooked functions
  1330. --*/
  1331. HOOK_BEGIN
  1332. CALL_NOTIFY_FUNCTION
  1333. DUMP_VERIFIER_LOG_ENTRY(VLOG_LOGFILECHANGES_LOGLOC,
  1334. AVS_LOGFILECHANGES_LOGLOC,
  1335. AVS_LOGFILECHANGES_LOGLOC_R,
  1336. AVS_LOGFILECHANGES_LOGLOC_URL)
  1337. DUMP_VERIFIER_LOG_ENTRY(VLOG_LOGFILECHANGES_UFW,
  1338. AVS_LOGFILECHANGES_UFW,
  1339. AVS_LOGFILECHANGES_UFW_R,
  1340. AVS_LOGFILECHANGES_UFW_URL)
  1341. APIHOOK_ENTRY(NTDLL.DLL, NtCreateFile)
  1342. APIHOOK_ENTRY(NTDLL.DLL, NtOpenFile)
  1343. APIHOOK_ENTRY(NTDLL.DLL, NtWriteFile)
  1344. APIHOOK_ENTRY(NTDLL.DLL, NtWriteFileGather)
  1345. APIHOOK_ENTRY(NTDLL.DLL, NtSetInformationFile)
  1346. APIHOOK_ENTRY(NTDLL.DLL, NtClose)
  1347. APIHOOK_ENTRY(NTDLL.DLL, NtDeleteFile)
  1348. HOOK_END
  1349. IMPLEMENT_SHIM_END