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.

800 lines
22 KiB

  1. /*++
  2. Copyright (c) 1989-1999 Microsoft Corporation
  3. Module Name:
  4. fspyUser.c
  5. Abstract:
  6. This file contains the implementation for the main function of the
  7. user application piece of FileSpy. This function is responsible for
  8. controlling the command mode available to the user to control the
  9. kernel mode driver.
  10. Environment:
  11. User mode
  12. // @@BEGIN_DDKSPLIT
  13. Author:
  14. George Jenkins (GeorgeJe)
  15. Revision History:
  16. Molly Brown (MollyBro) 21-Apr-1999
  17. Broke out the logging code and added command mode functionality.
  18. Neal Christiansen (nealch) 06-Jul-2001
  19. Updated cash statistics for use with contexts
  20. // @@END_DDKSPLIT
  21. --*/
  22. #include <windows.h>
  23. #include <stdlib.h>
  24. #include <stdio.h>
  25. #include <winioctl.h>
  26. #include <string.h>
  27. #include <crtdbg.h>
  28. #include "filespy.h"
  29. #include "fspyLog.h"
  30. #include "filespyLib.h"
  31. #define SUCCESS 0
  32. #define USAGE_ERROR 1
  33. #define EXIT_INTERPRETER 2
  34. #define EXIT_PROGRAM 4
  35. #define INTERPRETER_EXIT_COMMAND1 "go"
  36. #define INTERPRETER_EXIT_COMMAND2 "g"
  37. #define PROGRAM_EXIT_COMMAND "exit"
  38. #define ToggleFlag(V, F) (V = (((V) & (F)) ? (V & (~F)) : (V | F)))
  39. DWORD
  40. InterpretCommand(
  41. int argc,
  42. char *argv[],
  43. PLOG_CONTEXT Context
  44. );
  45. BOOL
  46. ListDevices(
  47. PLOG_CONTEXT Context
  48. );
  49. BOOL
  50. ListHashStats(
  51. PLOG_CONTEXT Context
  52. );
  53. VOID
  54. DisplayError (
  55. DWORD Code
  56. );
  57. int _cdecl main(int argc, char *argv[])
  58. {
  59. SC_HANDLE hSCManager = NULL;
  60. SC_HANDLE hService = NULL;
  61. SERVICE_STATUS_PROCESS serviceInfo;
  62. DWORD bytesNeeded;
  63. HANDLE hDevice = NULL;
  64. BOOL bResult;
  65. DWORD result;
  66. ULONG threadId;
  67. HANDLE thread = NULL;
  68. LOG_CONTEXT context;
  69. INT inputChar;
  70. //
  71. // Initialize handle in case of error
  72. //
  73. context.ShutDown = NULL;
  74. context.VerbosityFlags = 0;
  75. //
  76. // Start the kernel mode driver through the service manager
  77. //
  78. hSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS) ;
  79. if (NULL == hSCManager) {
  80. result = GetLastError();
  81. printf("ERROR opening Service Manager...\n");
  82. DisplayError( result );
  83. goto Main_Continue;
  84. }
  85. hService = OpenService( hSCManager,
  86. FILESPY_SERVICE_NAME,
  87. FILESPY_SERVICE_ACCESS);
  88. if (NULL == hService) {
  89. result = GetLastError();
  90. printf("ERROR opening FileSpy Service...\n");
  91. DisplayError( result );
  92. goto Main_Continue;
  93. }
  94. if (!QueryServiceStatusEx( hService,
  95. SC_STATUS_PROCESS_INFO,
  96. (UCHAR *)&serviceInfo,
  97. sizeof(serviceInfo),
  98. &bytesNeeded))
  99. {
  100. result = GetLastError();
  101. printf("ERROR querrying status of FileSpy Service...\n");
  102. DisplayError( result );
  103. goto Main_Continue;
  104. }
  105. if(serviceInfo.dwCurrentState != SERVICE_RUNNING) {
  106. //
  107. // Service hasn't been started yet, so try to start service
  108. //
  109. if (!StartService(hService, 0, NULL)) {
  110. result = GetLastError();
  111. printf("ERROR starting FileSpy service...\n");
  112. DisplayError( result );
  113. goto Main_Continue;
  114. }
  115. }
  116. Main_Continue:
  117. printf("Hit [Enter] to begin command mode...\n");
  118. //
  119. // Open the device that is used to talk to FileSpy.
  120. //
  121. printf("FileSpy: Opening device...\n");
  122. hDevice = CreateFile( FILESPY_W32_DEVICE_NAME,
  123. GENERIC_READ | GENERIC_WRITE,
  124. 0,
  125. NULL,
  126. OPEN_EXISTING,
  127. FILE_ATTRIBUTE_NORMAL,
  128. NULL);
  129. if (hDevice == INVALID_HANDLE_VALUE) {
  130. result = GetLastError();
  131. printf("ERROR opening device...\n");
  132. DisplayError( result );
  133. goto Main_Exit;
  134. }
  135. //
  136. // Initialize the fields of the LOG_CONTEXT.
  137. //
  138. context.Device = hDevice;
  139. context.ShutDown = CreateSemaphore(
  140. NULL,
  141. 0,
  142. 1,
  143. L"FileSpy shutdown");
  144. if (context.ShutDown == NULL) {
  145. //
  146. // Insufficient memory for this semaphore, so shutdown.
  147. //
  148. printf( "ERROR insufficient memory\n" );
  149. goto Main_Exit;
  150. }
  151. context.CleaningUp = FALSE;
  152. context.LogToScreen = context.NextLogToScreen = TRUE;
  153. context.LogToFile = FALSE;
  154. context.OutputFile = NULL;
  155. //
  156. // Check the valid parameters for startup
  157. //
  158. if (argc > 1) {
  159. if (InterpretCommand(argc - 1, &(argv[1]), &context) == USAGE_ERROR) {
  160. goto Main_Exit;
  161. }
  162. }
  163. //
  164. // Propagate the /s switch to the variable that the logging
  165. // thread checks.
  166. //
  167. context.LogToScreen = context.NextLogToScreen;
  168. //
  169. // Check to see what devices we are attached to from
  170. // previous runs of this program.
  171. //
  172. bResult = ListDevices(&context);
  173. if (!bResult) {
  174. result = GetLastError();
  175. printf("ERROR listing devices...\n");
  176. DisplayError( result );
  177. }
  178. //
  179. // Create the thread to read the log records that are gathered
  180. // by filespy.sys.
  181. //
  182. printf("FileSpy: Creating logging thread...\n");
  183. thread = CreateThread(
  184. NULL,
  185. 0,
  186. RetrieveLogRecords,
  187. (LPVOID)&context,
  188. 0,
  189. &threadId);
  190. if (!thread) {
  191. result = GetLastError();
  192. printf("ERROR creating logging thread...\n");
  193. DisplayError( result );
  194. goto Main_Exit;
  195. }
  196. while (inputChar = getchar()) {
  197. CHAR commandLine[81];
  198. INT parmCount, count, ch;
  199. CHAR **parms;
  200. BOOLEAN newParm;
  201. DWORD returnValue = SUCCESS;
  202. if (inputChar == '\n') {
  203. //
  204. // Start command interpreter. First we must turn off logging
  205. // to screen if we are. Also, remember the state of logging
  206. // to the screen, so that we can reinstate that when command
  207. // interpreter is finished.
  208. //
  209. context.NextLogToScreen = context.LogToScreen;
  210. context.LogToScreen = FALSE;
  211. while (returnValue != EXIT_INTERPRETER) {
  212. //
  213. // Print prompt
  214. //
  215. printf(">");
  216. //
  217. // Read in next line, keeping track of the number of parameters
  218. // as you go
  219. //
  220. parmCount = 1;
  221. for (count = 0;
  222. (count < 80) && ((ch = getchar())!= '\n');
  223. count++) {
  224. commandLine[count] = (CHAR)ch;
  225. if (ch == ' ') {
  226. parmCount ++;
  227. }
  228. }
  229. commandLine[count] = '\0';
  230. parms = (CHAR **)malloc(parmCount * sizeof(CHAR *));
  231. parmCount = 0;
  232. newParm = TRUE;
  233. for (count = 0; commandLine[count] != '\0'; count++) {
  234. if (newParm) {
  235. parms[parmCount] = &(commandLine[count]);
  236. parmCount ++;
  237. }
  238. if (commandLine[count] == ' ' ) {
  239. newParm = TRUE;
  240. } else {
  241. newParm = FALSE;
  242. }
  243. }
  244. //
  245. // We've got our parameter count and parameter list, so
  246. // send it off to be interpreted.
  247. //
  248. returnValue = InterpretCommand(parmCount, parms, &context);
  249. free(parms);
  250. if (returnValue == EXIT_PROGRAM) {
  251. // Time to stop the program
  252. goto Main_Cleanup;
  253. }
  254. }
  255. // Set LogToScreen appropriately based on any commands seen
  256. context.LogToScreen = context.NextLogToScreen;
  257. if (context.LogToScreen) {
  258. printf("Should be logging to screen...\n");
  259. }
  260. }
  261. }
  262. Main_Cleanup:
  263. //
  264. // Clean up the threads, then fall through to Main_Exit
  265. //
  266. printf("FileSpy: Cleaning up...\n");
  267. //
  268. // Set the Cleaning up flag to TRUE to notify other threads
  269. // that we are cleaning up
  270. //
  271. context.CleaningUp = TRUE;
  272. //
  273. // Wait for everyone to shut down
  274. //
  275. WaitForSingleObject(context.ShutDown, INFINITE);
  276. if (context.LogToFile) {
  277. fclose(context.OutputFile);
  278. }
  279. Main_Exit:
  280. //
  281. // Clean up the data that is always around and exit
  282. //
  283. if(context.ShutDown) {
  284. CloseHandle(context.ShutDown);
  285. }
  286. if (thread) {
  287. CloseHandle(thread);
  288. }
  289. if(hSCManager) {
  290. CloseServiceHandle(hSCManager);
  291. }
  292. if(hService) {
  293. CloseServiceHandle(hService);
  294. }
  295. if (hDevice) {
  296. CloseHandle(hDevice);
  297. }
  298. printf("FileSpy: All done\n");
  299. return 0;
  300. }
  301. DWORD
  302. InterpretCommand(
  303. int argc,
  304. char *argv[],
  305. PLOG_CONTEXT Context
  306. )
  307. {
  308. int parmIndex;
  309. CHAR *parm;
  310. BOOL bResult;
  311. DWORD result;
  312. DWORD returnValue = SUCCESS;
  313. CHAR buffer[BUFFER_SIZE];
  314. DWORD bufferLength;
  315. DWORD bytesReturned;
  316. //
  317. // Interpret the command line parameters
  318. //
  319. for (parmIndex = 0; parmIndex < argc; parmIndex++) {
  320. parm = argv[parmIndex];
  321. if (parm[0] == '/') {
  322. //
  323. // Have the beginning of a switch
  324. //
  325. switch (parm[1]) {
  326. case 'a':
  327. case 'A':
  328. //
  329. // Attach to the specified drive letter
  330. //
  331. parmIndex++;
  332. if (parmIndex >= argc) {
  333. //
  334. // Not enough parameters
  335. //
  336. goto InterpretCommand_Usage;
  337. }
  338. parm = argv[parmIndex];
  339. printf("\tAttaching to %s\n", parm);
  340. bufferLength = MultiByteToWideChar(
  341. CP_ACP,
  342. MB_ERR_INVALID_CHARS,
  343. parm,
  344. -1,
  345. (LPWSTR)buffer,
  346. BUFFER_SIZE/sizeof(WCHAR));
  347. bResult = DeviceIoControl(
  348. Context->Device,
  349. FILESPY_StartLoggingDevice,
  350. buffer,
  351. bufferLength * sizeof(WCHAR),
  352. NULL,
  353. 0,
  354. &bytesReturned,
  355. NULL);
  356. if (!bResult) {
  357. result = GetLastError();
  358. printf("ERROR attaching to device...\n");
  359. DisplayError( result );
  360. }
  361. break;
  362. case 'd':
  363. case 'D':
  364. //
  365. // Detach to the specified drive letter
  366. //
  367. parmIndex++;
  368. if (parmIndex >= argc) {
  369. //
  370. // Not enough parameters
  371. //
  372. goto InterpretCommand_Usage;
  373. }
  374. parm = argv[parmIndex];
  375. printf("\tDetaching from %s\n", parm);
  376. bufferLength = MultiByteToWideChar(
  377. CP_ACP,
  378. MB_ERR_INVALID_CHARS,
  379. parm,
  380. -1,
  381. (LPWSTR)buffer,
  382. BUFFER_SIZE/sizeof(WCHAR));
  383. bResult = DeviceIoControl(
  384. Context->Device,
  385. FILESPY_StopLoggingDevice,
  386. buffer,
  387. bufferLength * sizeof(WCHAR),
  388. NULL,
  389. 0,
  390. &bytesReturned,
  391. NULL);
  392. if (!bResult) {
  393. result = GetLastError();
  394. printf("ERROR detaching to device...\n");
  395. DisplayError( result );
  396. }
  397. break;
  398. case 'h':
  399. case 'H':
  400. ListHashStats(Context);
  401. break;
  402. case 'l':
  403. case 'L':
  404. //
  405. // List all devices that are currently being monitored
  406. //
  407. bResult = ListDevices(Context);
  408. if (!bResult) {
  409. result = GetLastError();
  410. printf("ERROR listing devices...\n");
  411. DisplayError( result );
  412. }
  413. break;
  414. case 's':
  415. case 'S':
  416. //
  417. // Output logging results to screen, save new value to
  418. // instate when command interpreter is exited.
  419. //
  420. if (Context->NextLogToScreen) {
  421. printf("\tTurning off logging to screen\n");
  422. } else {
  423. printf("\tTurning on logging to screen\n");
  424. }
  425. Context->NextLogToScreen = !Context->NextLogToScreen;
  426. break;
  427. case 'f':
  428. case 'F':
  429. //
  430. // Output logging results to file
  431. //
  432. if (Context->LogToFile) {
  433. printf("\tStop logging to file \n");
  434. Context->LogToFile = FALSE;
  435. _ASSERT(Context->OutputFile);
  436. fclose(Context->OutputFile);
  437. Context->OutputFile = NULL;
  438. } else {
  439. parmIndex++;
  440. if (parmIndex >= argc) {
  441. // Not enough parameters
  442. goto InterpretCommand_Usage;
  443. }
  444. parm = argv[parmIndex];
  445. Context->OutputFile = fopen(parm, "w");
  446. if (Context->OutputFile == NULL) {
  447. result = GetLastError();
  448. printf("\nERROR opening \"%s\"...\n",parm);
  449. DisplayError( result );
  450. returnValue = USAGE_ERROR;
  451. goto InterpretCommand_Exit;
  452. }
  453. Context->LogToFile = TRUE;
  454. printf("\tLog to file %s\n", parm);
  455. }
  456. break;
  457. case 'v':
  458. case 'V':
  459. //
  460. // Toggle the specified verbosity flag.
  461. //
  462. parmIndex++;
  463. if (parmIndex >= argc) {
  464. //
  465. // Not enough parameters
  466. //
  467. goto InterpretCommand_Usage;
  468. }
  469. parm = argv[parmIndex];
  470. switch(parm[0]) {
  471. case 'p':
  472. case 'P':
  473. ToggleFlag( Context->VerbosityFlags, FS_VF_DUMP_PARAMETERS );
  474. break;
  475. default:
  476. //
  477. // Invalid switch, goto usage
  478. //
  479. goto InterpretCommand_Usage;
  480. }
  481. break;
  482. default:
  483. //
  484. // Invalid switch, goto usage
  485. //
  486. goto InterpretCommand_Usage;
  487. }
  488. } else {
  489. //
  490. // Look for "go" or "g" to see if we should exit interpreter
  491. //
  492. if (!_strnicmp(
  493. parm,
  494. INTERPRETER_EXIT_COMMAND1,
  495. sizeof(INTERPRETER_EXIT_COMMAND1))) {
  496. returnValue = EXIT_INTERPRETER;
  497. goto InterpretCommand_Exit;
  498. }
  499. if (!_strnicmp(
  500. parm,
  501. INTERPRETER_EXIT_COMMAND2,
  502. sizeof(INTERPRETER_EXIT_COMMAND2))) {
  503. returnValue = EXIT_INTERPRETER;
  504. goto InterpretCommand_Exit;
  505. }
  506. //
  507. // Look for "exit" to see if we should exit program
  508. //
  509. if (!_strnicmp(
  510. parm,
  511. PROGRAM_EXIT_COMMAND,
  512. sizeof(PROGRAM_EXIT_COMMAND))) {
  513. returnValue = EXIT_PROGRAM;
  514. goto InterpretCommand_Exit;
  515. }
  516. //
  517. // Invalid parameter
  518. //
  519. goto InterpretCommand_Usage;
  520. }
  521. }
  522. InterpretCommand_Exit:
  523. return returnValue;
  524. InterpretCommand_Usage:
  525. printf("Valid switches: [/a <drive>] [/d <drive>] [/h] [/l] [/s] [/f [<file name>] [/v <flag>]]\n"
  526. "\t[/a <drive>] attaches monitor to <drive>\n"
  527. "\t[/d <drive>] detaches monitor from <drive>\n"
  528. "\t[/h] print filename hash statistics\n"
  529. "\t[/l] lists all the drives the monitor is currently attached to\n"
  530. "\t[/s] turns on and off showing logging output on the screen\n"
  531. "\t[/f [<file name>]] turns on and off logging to the specified file\n"
  532. "\t[/v <flag>] toggles a verbosity flag. Valid verbosity flags are:\n"
  533. "\t\tp (dump irp parameters)\n"
  534. "If you are in command mode,\n"
  535. "\t[go|g] will exit command mode\n"
  536. "\t[exit] will terminate this program\n"
  537. );
  538. returnValue = USAGE_ERROR;
  539. goto InterpretCommand_Exit;
  540. }
  541. BOOL
  542. ListHashStats(
  543. PLOG_CONTEXT Context
  544. )
  545. {
  546. ULONG bytesReturned;
  547. BOOL returnValue;
  548. FILESPY_STATISTICS stats;
  549. returnValue = DeviceIoControl( Context->Device,
  550. FILESPY_GetStats,
  551. NULL,
  552. 0,
  553. (CHAR *) &stats,
  554. BUFFER_SIZE,
  555. &bytesReturned,
  556. NULL );
  557. if (returnValue) {
  558. printf(" STATISTICS\n");
  559. printf("---------------------------------\n");
  560. printf("%-40s %8d\n",
  561. "Name lookups",
  562. stats.TotalContextSearches);
  563. printf("%-40s %8d\n",
  564. "Name lookup hits",
  565. stats.TotalContextFound);
  566. if (stats.TotalContextSearches) {
  567. printf(
  568. "%-40s %8.2f%%\n",
  569. "Hit ratio",
  570. ((FLOAT) stats.TotalContextFound / (FLOAT) stats.TotalContextSearches) * 100.);
  571. }
  572. printf("%-40s %8d\n",
  573. "Names created",
  574. stats.TotalContextCreated);
  575. printf("%-40s %8d\n",
  576. "Temporary Names created",
  577. stats.TotalContextTemporary);
  578. printf("%-40s %8d\n",
  579. "Duplicate names created",
  580. stats.TotalContextDuplicateFrees);
  581. printf("%-40s %8d\n",
  582. "Context callback frees",
  583. stats.TotalContextCtxCallbackFrees);
  584. printf("%-40s %8d\n",
  585. "NonDeferred context frees",
  586. stats.TotalContextNonDeferredFrees);
  587. printf("%-40s %8d\n",
  588. "Deferred context frees",
  589. stats.TotalContextDeferredFrees);
  590. printf("%-40s %8d\n",
  591. "Delete all contexts",
  592. stats.TotalContextDeleteAlls);
  593. printf("%-40s %8d\n",
  594. "Contexts not supported",
  595. stats.TotalContextsNotSupported);
  596. printf("%-40s %8d\n",
  597. "Contexts not found attached to stream",
  598. stats.TotalContextsNotFoundInStreamList);
  599. }
  600. return returnValue;
  601. }
  602. BOOL
  603. ListDevices(
  604. PLOG_CONTEXT Context
  605. )
  606. {
  607. CHAR buffer[BUFFER_SIZE];
  608. ULONG bytesReturned;
  609. BOOL returnValue;
  610. returnValue = DeviceIoControl(
  611. Context->Device,
  612. FILESPY_ListDevices,
  613. NULL,
  614. 0,
  615. buffer,
  616. BUFFER_SIZE,
  617. &bytesReturned,
  618. NULL);
  619. if (returnValue) {
  620. PATTACHED_DEVICE device = (PATTACHED_DEVICE) buffer;
  621. printf("DEVICE NAME | LOGGING STATUS\n");
  622. printf("------------------------------------------------------\n");
  623. if (bytesReturned == 0) {
  624. printf("No devices attached\n");
  625. } else {
  626. while ((BYTE *)device < buffer + bytesReturned) {
  627. printf(
  628. "%-38S| %s\n",
  629. device->DeviceNames,
  630. (device->LoggingOn)?"ON":"OFF");
  631. device ++;
  632. }
  633. }
  634. }
  635. return returnValue;
  636. }
  637. VOID
  638. DisplayError (
  639. DWORD Code
  640. )
  641. /*++
  642. Routine Description:
  643. This routine will display an error message based off of the Win32 error
  644. code that is passed in. This allows the user to see an understandable
  645. error message instead of just the code.
  646. Arguments:
  647. Code - The error code to be translated.
  648. Return Value:
  649. None.
  650. --*/
  651. {
  652. WCHAR buffer[80] ;
  653. DWORD count ;
  654. //
  655. // Translate the Win32 error code into a useful message.
  656. //
  657. count = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
  658. NULL,
  659. Code,
  660. 0,
  661. buffer,
  662. sizeof( buffer )/sizeof( WCHAR ),
  663. NULL) ;
  664. //
  665. // Make sure that the message could be translated.
  666. //
  667. if (count == 0) {
  668. printf("\nError could not be translated.\n Code: %d\n", Code) ;
  669. return;
  670. }
  671. else {
  672. //
  673. // Display the translated error.
  674. //
  675. printf("%S\n", buffer) ;
  676. return;
  677. }
  678. }