Leaked source code of windows server 2003
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.

1848 lines
51 KiB

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