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.

1408 lines
39 KiB

  1. /*
  2. Copyright (c) 2000 Microsoft Corporation
  3. File name:
  4. patch.c
  5. Author:
  6. Adrian Marinescu (adrmarin) Wed Nov 14 2001
  7. */
  8. #include "nt.h"
  9. #include "ntrtl.h"
  10. #include "nturtl.h"
  11. #include <windows.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <math.h>
  16. #include <sfc.h>
  17. #include <psapi.h>
  18. //
  19. // Global constants
  20. //
  21. #define PATCH_OC_INSTALL 1
  22. #define PATCH_OC_UNINSTALL 2
  23. #define PATCH_OC_REPLACE_FILE 3
  24. ULONG OperationCode = 0;
  25. //
  26. // System file protection utilities
  27. //
  28. typedef HANDLE (WINAPI *CONNECTTOSFCSERVER)(PCWSTR);
  29. typedef DWORD (WINAPI *SFCFILEEXCEPTION)(HANDLE, PCWSTR, DWORD);
  30. typedef VOID (WINAPI * SFCCLOSE)(HANDLE);
  31. SFCFILEEXCEPTION pSfcFileException = NULL;
  32. CONNECTTOSFCSERVER pConnectToSfcServer = NULL;
  33. SFCCLOSE pSfcClose = NULL;
  34. HANDLE
  35. LoadSfcLibrary()
  36. {
  37. HANDLE hLibSfc;
  38. hLibSfc = LoadLibrary("SFC.DLL");
  39. if ( hLibSfc != NULL ) {
  40. pConnectToSfcServer = (CONNECTTOSFCSERVER)GetProcAddress( hLibSfc, (LPCSTR)0x00000003 );
  41. pSfcClose = (SFCCLOSE)GetProcAddress( hLibSfc, (LPCSTR)0x00000004 );
  42. pSfcFileException = (SFCFILEEXCEPTION)GetProcAddress( hLibSfc, (LPCSTR)0x00000005 );
  43. }
  44. return hLibSfc;
  45. }
  46. NTSTATUS
  47. RemoveKnownDll (
  48. PWSTR FileName
  49. )
  50. {
  51. WCHAR Buffer[DOS_MAX_PATH_LENGTH + 1];
  52. int BytesCopied;
  53. UNICODE_STRING Unicode;
  54. NTSTATUS Status;
  55. OBJECT_ATTRIBUTES Obja;
  56. HANDLE Section;
  57. BytesCopied = _snwprintf( Buffer,
  58. DOS_MAX_PATH_LENGTH,
  59. L"\\KnownDlls\\%ws",
  60. FileName );
  61. if ( BytesCopied < 0 ) {
  62. return STATUS_BUFFER_TOO_SMALL;
  63. }
  64. RtlInitUnicodeString(&Unicode, Buffer);
  65. //
  66. // open the section object
  67. //
  68. InitializeObjectAttributes (&Obja,
  69. &Unicode,
  70. OBJ_CASE_INSENSITIVE,
  71. NULL,
  72. NULL);
  73. Status = NtOpenSection (&Section,
  74. SECTION_ALL_ACCESS,
  75. &Obja);
  76. if ( !NT_SUCCESS(Status) ) {
  77. printf("%ws is not a known dll\n", FileName);
  78. return Status;
  79. }
  80. printf("%ws is a known dll. Deleting ...\n", FileName);
  81. Status = NtMakeTemporaryObject(Section);
  82. if ( !NT_SUCCESS(Status) ) {
  83. printf("NtMakeTemporaryObject failed with status %lx\n", Status);
  84. }
  85. NtClose(Section);
  86. return Status;
  87. }
  88. NTSTATUS
  89. RemoveDelayedRename(
  90. IN PUNICODE_STRING OldFileName,
  91. IN PUNICODE_STRING NewFileName,
  92. IN ULONG Index
  93. )
  94. /*++
  95. Routine Description:
  96. Appends the given delayed move file operation to the registry
  97. value that contains the list of move file operations to be
  98. performed on the next boot.
  99. Arguments:
  100. OldFileName - Supplies the old file name
  101. NewFileName - Supplies the new file name
  102. Return Value:
  103. NTSTATUS
  104. --*/
  105. {
  106. OBJECT_ATTRIBUTES Obja;
  107. UNICODE_STRING KeyName;
  108. UNICODE_STRING ValueName;
  109. HANDLE KeyHandle;
  110. PWSTR ValueData, s;
  111. PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
  112. ULONG ValueLength = 1024;
  113. ULONG ReturnedLength;
  114. WCHAR ValueNameBuf[64];
  115. NTSTATUS Status;
  116. RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager" );
  117. if ( Index == 1 ) {
  118. RtlInitUnicodeString( &ValueName, L"PendingFileRenameOperations" );
  119. } else {
  120. swprintf(ValueNameBuf,L"PendingFileRenameOperations%d",Index);
  121. RtlInitUnicodeString( &ValueName, ValueNameBuf );
  122. }
  123. InitializeObjectAttributes(
  124. &Obja,
  125. &KeyName,
  126. OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
  127. NULL,
  128. NULL
  129. );
  130. Status = NtCreateKey( &KeyHandle,
  131. GENERIC_READ | GENERIC_WRITE,
  132. &Obja,
  133. 0,
  134. NULL,
  135. 0,
  136. NULL
  137. );
  138. if ( Status == STATUS_ACCESS_DENIED ) {
  139. Status = NtCreateKey( &KeyHandle,
  140. GENERIC_READ | GENERIC_WRITE,
  141. &Obja,
  142. 0,
  143. NULL,
  144. REG_OPTION_BACKUP_RESTORE,
  145. NULL
  146. );
  147. }
  148. if ( !NT_SUCCESS( Status ) ) {
  149. return Status;
  150. }
  151. while ( TRUE ) {
  152. ValueInfo = RtlAllocateHeap(RtlProcessHeap(),
  153. 0,
  154. ValueLength);
  155. if ( ValueInfo == NULL ) {
  156. NtClose(KeyHandle);
  157. return(STATUS_NO_MEMORY);
  158. }
  159. //
  160. // File rename operations are stored in the registry in a
  161. // single MULTI_SZ value. This allows the renames to be
  162. // performed in the same order that they were originally
  163. // requested. Each rename operation consists of a pair of
  164. // NULL-terminated strings.
  165. //
  166. Status = NtQueryValueKey(KeyHandle,
  167. &ValueName,
  168. KeyValuePartialInformation,
  169. ValueInfo,
  170. ValueLength,
  171. &ReturnedLength);
  172. if ( Status != STATUS_BUFFER_OVERFLOW ) {
  173. break;
  174. }
  175. //
  176. // The existing value is too large for our buffer.
  177. // Retry with a larger buffer.
  178. //
  179. ValueLength = ReturnedLength;
  180. RtlFreeHeap(RtlProcessHeap(), 0, ValueInfo);
  181. }
  182. if ( NT_SUCCESS(Status) ) {
  183. //
  184. // A value already exists, append our two strings to the
  185. // MULTI_SZ.
  186. //
  187. ValueData = (PWSTR)(&ValueInfo->Data);
  188. s = (PWSTR)((PCHAR)ValueData + ValueInfo->DataLength) - 1;
  189. while ( ValueData < s ) {
  190. UNICODE_STRING CrtString;
  191. PWSTR Base;
  192. ULONG RemovedBytes;
  193. Base = ValueData;
  194. RtlInitUnicodeString(&CrtString, ValueData);
  195. RemovedBytes = CrtString.Length + sizeof(WCHAR);
  196. if ( RtlEqualUnicodeString( &CrtString,
  197. OldFileName,
  198. TRUE ) ) {
  199. ValueData += CrtString.Length / sizeof(WCHAR) + 1;
  200. RtlInitUnicodeString(&CrtString, ValueData + 1);
  201. if ( RtlEqualUnicodeString( &CrtString,
  202. NewFileName,
  203. TRUE ) ) {
  204. RemovedBytes += CrtString.Length + 2 * sizeof(WCHAR); // NULL + !
  205. printf("Removing delayed entry %ws -> %ws\n", Base, ValueData);
  206. ValueData += CrtString.Length / sizeof(WCHAR) + 1;
  207. MoveMemory(Base, ValueData, (ULONG_PTR)s - (ULONG_PTR)ValueData);
  208. printf("Deleting delayed rename key (%ld bytes left)\n", (ULONG)ValueInfo->DataLength - RemovedBytes);
  209. Status = NtSetValueKey(KeyHandle,
  210. &ValueName,
  211. 0,
  212. REG_MULTI_SZ,
  213. &ValueInfo->Data,
  214. (ULONG)ValueInfo->DataLength - RemovedBytes);
  215. break;
  216. }
  217. } else {
  218. ValueData += CrtString.Length / sizeof(WCHAR) + 1;
  219. }
  220. }
  221. } else {
  222. NtClose(KeyHandle);
  223. RtlFreeHeap(RtlProcessHeap(), 0, ValueInfo);
  224. return(Status);
  225. }
  226. NtClose(KeyHandle);
  227. RtlFreeHeap(RtlProcessHeap(), 0, ValueInfo);
  228. return(Status);
  229. }
  230. NTSTATUS
  231. ReplaceSystemFile(
  232. PWSTR TargetPath,
  233. PWSTR ReplacedFileName,
  234. PWSTR ReplacementFile
  235. )
  236. {
  237. WCHAR FullOriginalName[DOS_MAX_PATH_LENGTH + 1];
  238. WCHAR TmpReplacementFile[DOS_MAX_PATH_LENGTH + 1];
  239. WCHAR TmpOrigName[DOS_MAX_PATH_LENGTH + 1];
  240. int BytesCopied;
  241. UNICODE_STRING ReplacedUnicodeString, NewUnicodeString, TempOrigFile;
  242. HANDLE hSfp;
  243. OBJECT_ATTRIBUTES ObjectAttributes;
  244. IO_STATUS_BLOCK IoStatus;
  245. HANDLE ReplacedFileHandle = NULL, NewFileHandle = NULL;
  246. PFILE_RENAME_INFORMATION RenameInfo1 = NULL, RenameInfo2 = NULL;
  247. NTSTATUS Status = STATUS_SUCCESS;
  248. DWORD Result;
  249. ULONG i;
  250. int ThreadPriority;
  251. RtlInitUnicodeString(&ReplacedUnicodeString, NULL);
  252. RtlInitUnicodeString(&NewUnicodeString, NULL);
  253. BytesCopied = _snwprintf( FullOriginalName,
  254. DOS_MAX_PATH_LENGTH,
  255. L"%ws\\%ws",
  256. TargetPath,
  257. ReplacedFileName );
  258. if ( BytesCopied < 0 ) {
  259. return STATUS_BUFFER_TOO_SMALL;
  260. }
  261. if ( !RtlDosPathNameToNtPathName_U(
  262. FullOriginalName,
  263. &ReplacedUnicodeString,
  264. NULL,
  265. NULL
  266. ) ) {
  267. printf("RtlDosPathNameToNtPathName_U failed\n");
  268. Status = STATUS_UNSUCCESSFUL;
  269. goto cleanup;
  270. }
  271. //
  272. // Open the target file and keep a handle opened to it
  273. //
  274. InitializeObjectAttributes (&ObjectAttributes,
  275. &ReplacedUnicodeString,
  276. OBJ_CASE_INSENSITIVE,
  277. NULL,
  278. NULL);
  279. Status = NtOpenFile( &ReplacedFileHandle,
  280. SYNCHRONIZE | DELETE | FILE_GENERIC_READ,
  281. &ObjectAttributes,
  282. &IoStatus,
  283. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  284. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
  285. if ( !NT_SUCCESS(Status) ) {
  286. printf( "Opening the \"%ws\" file failed %lx (IOStatus = %lx)\n",
  287. FullOriginalName,
  288. Status,
  289. IoStatus);
  290. goto cleanup;
  291. }
  292. //
  293. // If a knowndll file, then remove it from the system known dll directory
  294. //
  295. Status = RemoveKnownDll( ReplacedFileName );
  296. if ( !NT_SUCCESS(Status) &&
  297. (Status != STATUS_OBJECT_NAME_NOT_FOUND) ) {
  298. printf( "Removing the KnownDll entry for \"%ws\" failed %lx\n",
  299. ReplacedFileName,
  300. Status);
  301. goto cleanup;
  302. }
  303. //
  304. // Unprotect the replaced file
  305. //
  306. hSfp = (pConnectToSfcServer)( NULL );
  307. if ( hSfp ) {
  308. if ( SfcIsFileProtected(hSfp, FullOriginalName) ) {
  309. printf("Replacing protected file \"%ws\"\n", FullOriginalName);
  310. Result = (pSfcFileException)(
  311. hSfp,
  312. (PWSTR) FullOriginalName,
  313. (DWORD) -1
  314. );
  315. if ( Result != NO_ERROR ) {
  316. printf("Unprotect file \"%ws\" failed, ec = %d\n", FullOriginalName, Result);
  317. (pSfcClose)(hSfp);
  318. Status = STATUS_UNSUCCESSFUL;
  319. goto cleanup;
  320. }
  321. } else {
  322. printf("Replacing unprotected file \"%ws\"\n", FullOriginalName);
  323. }
  324. (pSfcClose)(hSfp);
  325. } else {
  326. printf("SfcConnectToServer failed\n");
  327. Status = STATUS_UNSUCCESSFUL;
  328. goto cleanup;
  329. }
  330. if ( GetTempFileNameW(TargetPath, L"HOTP", 0, TmpReplacementFile) == 0 ) {
  331. printf("GetTempFileNameW failed\n");
  332. Status = STATUS_UNSUCCESSFUL;
  333. goto cleanup;
  334. }
  335. if ( !DeleteFileW(TmpReplacementFile) ) {
  336. printf("DeleteFile \"%ws\" failed with error %ld\n", TmpReplacementFile, GetLastError());
  337. Status = STATUS_UNSUCCESSFUL;
  338. goto cleanup;
  339. }
  340. if ( !CopyFileW ( ReplacementFile,
  341. TmpReplacementFile,
  342. TRUE ) ) {
  343. printf("CopyFileW \"%ws\" failed with error %ld\n", TmpReplacementFile, GetLastError());
  344. Status = STATUS_UNSUCCESSFUL;
  345. goto cleanup;
  346. }
  347. if ( !RtlDosPathNameToNtPathName_U(
  348. TmpReplacementFile,
  349. &NewUnicodeString,
  350. NULL,
  351. NULL
  352. ) ) {
  353. printf("RtlDosPathNameToNtPathName_U failed\n");
  354. Status = STATUS_UNSUCCESSFUL;
  355. goto cleanup;
  356. }
  357. //
  358. // Open the new file and keep a handle opened to it
  359. //
  360. InitializeObjectAttributes (&ObjectAttributes,
  361. &NewUnicodeString,
  362. OBJ_CASE_INSENSITIVE,
  363. NULL,
  364. NULL);
  365. Status = NtOpenFile( &NewFileHandle,
  366. SYNCHRONIZE | DELETE | FILE_GENERIC_READ,
  367. &ObjectAttributes,
  368. &IoStatus,
  369. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  370. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
  371. if ( !NT_SUCCESS(Status) ) {
  372. printf("Opening the temporary file \"%ws\" failed %lx (IOStatus = %lx)\n", TmpReplacementFile, Status, IoStatus);
  373. goto cleanup;
  374. }
  375. //
  376. // Prepare the rename info for the original file
  377. // It will be a temporary
  378. //
  379. if ( GetTempFileNameW(TargetPath, L"HPO", 0, TmpOrigName) == 0 ) {
  380. printf("GetTempFileNameW failed\n");
  381. Status = STATUS_UNSUCCESSFUL;
  382. goto cleanup;
  383. }
  384. if ( !DeleteFileW(TmpOrigName) ) {
  385. printf("DeleteFile \"%ws\" failed with error %ld\n", TmpOrigName, GetLastError());
  386. Status = STATUS_UNSUCCESSFUL;
  387. goto cleanup;
  388. }
  389. if ( !RtlDosPathNameToNtPathName_U(
  390. TmpOrigName,
  391. &TempOrigFile,
  392. NULL,
  393. NULL
  394. ) ) {
  395. printf("RtlDosPathNameToNtPathName_U failed\n");
  396. Status = STATUS_UNSUCCESSFUL;
  397. goto cleanup;
  398. }
  399. RenameInfo1 = RtlAllocateHeap( RtlProcessHeap(),
  400. 0,
  401. TempOrigFile.Length+sizeof(*RenameInfo1));
  402. if ( RenameInfo1 == NULL ) {
  403. Status = STATUS_INSUFFICIENT_RESOURCES;
  404. goto cleanup;
  405. }
  406. RtlCopyMemory( RenameInfo1->FileName, TempOrigFile.Buffer, TempOrigFile.Length );
  407. RenameInfo1->ReplaceIfExists = TRUE;
  408. RenameInfo1->RootDirectory = NULL;
  409. RenameInfo1->FileNameLength = TempOrigFile.Length;
  410. RenameInfo2 = RtlAllocateHeap( RtlProcessHeap(),
  411. 0,
  412. ReplacedUnicodeString.Length+sizeof(*RenameInfo2));
  413. if ( RenameInfo2 == NULL ) {
  414. Status = STATUS_INSUFFICIENT_RESOURCES;
  415. goto cleanup;
  416. }
  417. RtlCopyMemory( RenameInfo2->FileName, ReplacedUnicodeString.Buffer, ReplacedUnicodeString.Length );
  418. RenameInfo2->ReplaceIfExists = TRUE;
  419. RenameInfo2->RootDirectory = NULL;
  420. RenameInfo2->FileNameLength = ReplacedUnicodeString.Length;
  421. //
  422. // We have everything set to do the two rename operations. However if
  423. // the machine crashes before the second rename operation the system may not recover at boot
  424. // We queue a delayed rename so smss will do the job at next boot. If we succeed,
  425. // then smss will not find the file so it will skip that step.
  426. //
  427. if ( !MoveFileExW( TmpReplacementFile,
  428. FullOriginalName,
  429. MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT) ) {
  430. //
  431. // We cannot queue the rename operation, so we cannot recover if
  432. // the machine crashes between the two renames below. Better refuse to apply the patch
  433. //
  434. printf("Failed to queue the rename operation for the temporary file (%ld)\n", GetLastError());
  435. Status = STATUS_UNSUCCESSFUL;
  436. goto cleanup;
  437. }
  438. ThreadPriority = GetThreadPriority(GetCurrentThread());
  439. if ( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) ) {
  440. printf("SetThreadPriority failed\n");
  441. }
  442. Status = NtSetInformationFile( ReplacedFileHandle,
  443. &IoStatus,
  444. RenameInfo1,
  445. TempOrigFile.Length+sizeof(*RenameInfo1),
  446. FileRenameInformation);
  447. if ( !NT_SUCCESS(Status) ) {
  448. printf("NtSetInformationFile failed for the original file %lx %lx\n", Status, IoStatus);
  449. goto cleanup;
  450. }
  451. Status = NtSetInformationFile( NewFileHandle,
  452. &IoStatus,
  453. RenameInfo2,
  454. ReplacedUnicodeString.Length+sizeof(*RenameInfo1),
  455. FileRenameInformation);
  456. if ( !NT_SUCCESS(Status) ) {
  457. printf("NtSetInformationFile failed for the new file %lx (IOStatus %lx). Restoring the original.\n", Status, IoStatus);
  458. //
  459. // Restore the original file
  460. //
  461. Status = NtSetInformationFile( ReplacedFileHandle,
  462. &IoStatus,
  463. RenameInfo2,
  464. ReplacedUnicodeString.Length+sizeof(*RenameInfo1),
  465. FileRenameInformation);
  466. goto cleanup;
  467. }
  468. if ( !SetThreadPriority(GetCurrentThread(), ThreadPriority) ) {
  469. printf("Restoring the thread priority failed\n");
  470. }
  471. if ( NT_SUCCESS(Status) ) {
  472. for ( i = 0; i < 3; i++ ) {
  473. Status = RemoveDelayedRename( &NewUnicodeString,
  474. &ReplacedUnicodeString,
  475. i );
  476. if ( NT_SUCCESS(Status) ) {
  477. break;
  478. }
  479. }
  480. }
  481. if ( ReplacedFileHandle ) {
  482. NtClose(ReplacedFileHandle);
  483. }
  484. if ( NewFileHandle ) {
  485. NtClose(NewFileHandle);
  486. }
  487. if ( !DeleteFileW(TmpOrigName) ) {
  488. printf("Queueing the temp file deletion for \"%ws\" \n", TmpOrigName);
  489. //
  490. // Detele the temporary file after next reboot
  491. //
  492. if ( !MoveFileExW( TmpOrigName,
  493. NULL,
  494. MOVEFILE_DELAY_UNTIL_REBOOT) ) {
  495. //
  496. // We cannot queue the delete operation operation
  497. //
  498. printf("Failed to queue the delete operation for the temporary file (%ld)\n", GetLastError());
  499. Status = STATUS_UNSUCCESSFUL;
  500. goto cleanup;
  501. }
  502. Status = STATUS_UNSUCCESSFUL;
  503. goto cleanup;
  504. }
  505. cleanup:
  506. if ( RenameInfo1 ) {
  507. RtlFreeHeap( RtlProcessHeap(), 0, RenameInfo1 );
  508. }
  509. if ( RenameInfo2 ) {
  510. RtlFreeHeap( RtlProcessHeap(), 0, RenameInfo2 );
  511. }
  512. RtlFreeUnicodeString(&NewUnicodeString);
  513. RtlFreeUnicodeString(&ReplacedUnicodeString);
  514. return Status;
  515. }
  516. BOOL
  517. PSTRToUnicodeString(
  518. OUT PUNICODE_STRING UnicodeString,
  519. IN LPCSTR lpSourceString
  520. )
  521. /*++
  522. Routine Description:
  523. Captures and converts a 8-bit (OEM or ANSI) string into a heap-allocated
  524. UNICODE string
  525. Arguments:
  526. UnicodeString - location where UNICODE_STRING is stored
  527. lpSourceString - string in OEM or ANSI
  528. Return Value:
  529. TRUE if string is correctly stored, FALSE if an error occurred. In the
  530. error case, the last error is correctly set.
  531. --*/
  532. {
  533. ANSI_STRING AnsiString;
  534. NTSTATUS Status;
  535. //
  536. // Convert input into dynamic unicode string
  537. //
  538. RtlInitString( &AnsiString, lpSourceString );
  539. RtlAnsiStringToUnicodeString(UnicodeString, &AnsiString, TRUE);
  540. return TRUE;
  541. }
  542. BOOLEAN
  543. InitializeAsDebugger(VOID)
  544. {
  545. HANDLE Token;
  546. PTOKEN_PRIVILEGES NewPrivileges;
  547. BYTE OldPriv[1024];
  548. PBYTE pbOldPriv;
  549. ULONG cbNeeded;
  550. BOOLEAN bRet = FALSE;
  551. LUID LuidPrivilege, LoadDriverPrivilege;
  552. DWORD PID = 0;
  553. //
  554. // Make sure we have access to adjust and to get the old token privileges
  555. //
  556. if ( !OpenProcessToken( GetCurrentProcess(),
  557. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  558. &Token) ) {
  559. printf("Cannot open process token %ld\n", GetLastError());
  560. return( FALSE );
  561. }
  562. cbNeeded = 0;
  563. //
  564. // Initialize the privilege adjustment structure
  565. //
  566. LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &LuidPrivilege );
  567. LookupPrivilegeValue(NULL, SE_LOAD_DRIVER_NAME, &LoadDriverPrivilege );
  568. NewPrivileges = (PTOKEN_PRIVILEGES)calloc( 1,
  569. sizeof(TOKEN_PRIVILEGES) +
  570. (2 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES));
  571. if ( NewPrivileges == NULL ) {
  572. CloseHandle(Token);
  573. return( bRet );
  574. }
  575. NewPrivileges->PrivilegeCount = 2;
  576. NewPrivileges->Privileges[0].Luid = LuidPrivilege;
  577. NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  578. NewPrivileges->Privileges[1].Luid = LoadDriverPrivilege;
  579. NewPrivileges->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
  580. //
  581. // Enable the privilege
  582. //
  583. pbOldPriv = OldPriv;
  584. bRet = (BOOLEAN)AdjustTokenPrivileges( Token,
  585. FALSE,
  586. NewPrivileges,
  587. 1024,
  588. (PTOKEN_PRIVILEGES)pbOldPriv,
  589. &cbNeeded );
  590. if ( !bRet ) {
  591. //
  592. // If the stack was too small to hold the privileges
  593. // then allocate off the heap
  594. //
  595. printf("AdjustTokenPrivileges returned %ld\n", GetLastError());
  596. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
  597. pbOldPriv = calloc(1,cbNeeded);
  598. if ( pbOldPriv == NULL ) {
  599. CloseHandle(Token);
  600. return( bRet);
  601. }
  602. bRet = (BOOLEAN)AdjustTokenPrivileges( Token,
  603. FALSE,
  604. NewPrivileges,
  605. cbNeeded,
  606. (PTOKEN_PRIVILEGES)pbOldPriv,
  607. &cbNeeded );
  608. } else {
  609. printf("Cannot adjust token privileges %ld\n", GetLastError());
  610. }
  611. }
  612. CloseHandle( Token );
  613. return(bRet);
  614. }
  615. void Usage ()
  616. {
  617. printf("Usage:\n");
  618. printf(" patch -k [-i|-u] pach_file\n");
  619. printf(" Apply a patch to a system driver.\n");
  620. printf(" -i Enable patch\n");
  621. printf(" -u Disable patch\n\n");
  622. printf(" patch -i pach_file [PID|image_name]\n");
  623. printf(" Apply a patch to a process.\n");
  624. printf(" If Image name is missing, all existing processes will be patched.\n\n");
  625. printf(" patch -u pach_file [PID|image_name] \n");
  626. printf(" Uninstall an existing patch.\n");
  627. printf(" If Image name is missing, all existing processes will be patched.\n\n");
  628. printf(" patch -r TargetPath TargetBinary SourcePath\n");
  629. printf(" Replaces the TargetPath\\TargetBinary with SourcePath\n");
  630. printf(" \n");
  631. printf(" \n");
  632. }
  633. PVOID
  634. MapPatchFile(
  635. HANDLE ProcessHandle,
  636. LPCTSTR wPatchName,
  637. ULONG PatchFlags
  638. )
  639. {
  640. PSYSTEM_HOTPATCH_CODE_INFORMATION RemoteInfo;
  641. SYSTEM_HOTPATCH_CODE_INFORMATION LocaLRemoteInfo;
  642. CANSI_STRING AnsiString;
  643. WCHAR Buffer[1024];
  644. SIZE_T Size;
  645. UNICODE_STRING DestinationString;
  646. DestinationString.Buffer = Buffer;
  647. DestinationString.Length = 0;
  648. DestinationString.MaximumLength = sizeof(Buffer);
  649. RtlInitAnsiString(&AnsiString, wPatchName);
  650. RtlAnsiStringToUnicodeString(
  651. &DestinationString,
  652. &AnsiString,
  653. FALSE
  654. );
  655. RemoteInfo = VirtualAllocEx( ProcessHandle,
  656. NULL,
  657. 4096 * 16,
  658. MEM_RESERVE | MEM_COMMIT,
  659. PAGE_READWRITE);
  660. if ( RemoteInfo == NULL ) {
  661. printf("VirtualAllocEx failed %ld\n", GetLastError());
  662. return NULL;
  663. }
  664. LocaLRemoteInfo.Flags = PatchFlags | FLG_HOTPATCH_NAME_INFO;
  665. LocaLRemoteInfo.NameInfo.NameLength = DestinationString.Length;
  666. LocaLRemoteInfo.NameInfo.NameOffset = sizeof(LocaLRemoteInfo);
  667. LocaLRemoteInfo.InfoSize = LocaLRemoteInfo.NameInfo.NameLength + LocaLRemoteInfo.NameInfo.NameOffset;
  668. if ( !WriteProcessMemory(ProcessHandle,
  669. RemoteInfo,
  670. &LocaLRemoteInfo,
  671. sizeof(LocaLRemoteInfo),
  672. &Size) ) {
  673. printf("Write process memory failed %ld\n", GetLastError());
  674. return NULL;
  675. }
  676. if ( !WriteProcessMemory(ProcessHandle,
  677. (RemoteInfo + 1),
  678. DestinationString.Buffer,
  679. DestinationString.Length + sizeof(LocaLRemoteInfo),
  680. &Size) ) {
  681. printf("Write process memory failed %ld\n", GetLastError());
  682. return NULL;
  683. }
  684. return RemoteInfo;
  685. }
  686. PSYSTEM_HOTPATCH_CODE_INFORMATION
  687. InitializeKernelPatchData(
  688. LPCTSTR wPatchName,
  689. ULONG PatchFlags
  690. )
  691. {
  692. PSYSTEM_HOTPATCH_CODE_INFORMATION KernelPatch;
  693. CANSI_STRING AnsiString;
  694. WCHAR Buffer[1024];
  695. SIZE_T Size;
  696. UNICODE_STRING DestinationString;
  697. DestinationString.Buffer = Buffer;
  698. DestinationString.Length = 0;
  699. DestinationString.MaximumLength = sizeof(Buffer);
  700. RtlInitAnsiString(&AnsiString, wPatchName);
  701. RtlAnsiStringToUnicodeString(
  702. &DestinationString,
  703. &AnsiString,
  704. FALSE
  705. );
  706. if ( !RtlDosPathNameToNtPathName_U(
  707. Buffer,
  708. &DestinationString,
  709. NULL,
  710. NULL
  711. ) ) {
  712. printf("RtlDosPathNameToNtPathName_U failed\n");
  713. return NULL;
  714. }
  715. KernelPatch = malloc(sizeof(SYSTEM_HOTPATCH_CODE_INFORMATION) + DestinationString.Length);
  716. if ( KernelPatch == NULL ) {
  717. printf("Not enough memory\n");
  718. RtlFreeUnicodeString(&DestinationString);
  719. return NULL;
  720. }
  721. KernelPatch->Flags = PatchFlags | FLG_HOTPATCH_NAME_INFO;
  722. KernelPatch->NameInfo.NameOffset = sizeof(SYSTEM_HOTPATCH_CODE_INFORMATION);
  723. KernelPatch->NameInfo.NameLength = DestinationString.Length;
  724. KernelPatch->InfoSize = sizeof(SYSTEM_HOTPATCH_CODE_INFORMATION) + DestinationString.Length;
  725. memcpy( (KernelPatch + 1), DestinationString.Buffer, DestinationString.Length);
  726. RtlFreeUnicodeString(&DestinationString);
  727. return KernelPatch;
  728. }
  729. int ApplyPatchToProcess(
  730. DWORD PID,
  731. PCHAR PatchFile)
  732. {
  733. DWORD ThreadId;
  734. HANDLE ProcessHandle = NULL;
  735. HANDLE RemoteThread = NULL;
  736. PVOID ThreadParam = NULL;
  737. HANDLE PortHandle = NULL;
  738. HMODULE NtDllHandle;
  739. LPTHREAD_START_ROUTINE PatchRoutine;
  740. DWORD ExitStatus;
  741. NTSTATUS Status;
  742. //
  743. // User mode patch
  744. //
  745. ProcessHandle = OpenProcess( PROCESS_QUERY_INFORMATION |
  746. PROCESS_VM_OPERATION |
  747. PROCESS_CREATE_THREAD |
  748. PROCESS_VM_WRITE |
  749. PROCESS_VM_READ,
  750. FALSE,
  751. PID );
  752. if ( ProcessHandle == NULL ) {
  753. printf("Cannot open process. Error %ld\n", GetLastError());
  754. return EXIT_FAILURE;
  755. }
  756. ThreadParam = MapPatchFile( ProcessHandle,
  757. PatchFile,
  758. ((OperationCode == PATCH_OC_INSTALL) ? 1 : 0)
  759. );
  760. if ( ThreadParam == NULL ) {
  761. return EXIT_FAILURE;
  762. }
  763. NtDllHandle = GetModuleHandle("ntdll.dll");
  764. if ( NtDllHandle == NULL ) {
  765. printf("Cannot get ntdll.dll handle\n");
  766. return EXIT_FAILURE;
  767. }
  768. PatchRoutine = (LPTHREAD_START_ROUTINE)GetProcAddress(NtDllHandle, "LdrHotPatchRoutine");
  769. if ( PatchRoutine == NULL ) {
  770. printf("Cannot get LdrHotPatchRoutine\n");
  771. return EXIT_FAILURE;
  772. }
  773. //
  774. // Use the Rtl version of create remote thread since the win32 version
  775. // does not work cross-session
  776. //
  777. Status = RtlCreateUserThread (ProcessHandle,
  778. NULL,
  779. FALSE,
  780. 0,
  781. 0,
  782. 0,
  783. (PUSER_THREAD_START_ROUTINE) PatchRoutine,
  784. ThreadParam,
  785. &RemoteThread,
  786. NULL);
  787. if (!NT_SUCCESS (Status)) {
  788. printf("Cannot create user thread. Error %ld\n", GetLastError());
  789. VirtualFreeEx( ProcessHandle,
  790. ThreadParam,
  791. 0,
  792. MEM_RELEASE
  793. );
  794. return EXIT_FAILURE;
  795. }
  796. WaitForSingleObject(RemoteThread, INFINITE);
  797. VirtualFreeEx( ProcessHandle,
  798. ThreadParam,
  799. 0,
  800. MEM_RELEASE
  801. );
  802. if ( GetExitCodeThread(RemoteThread, &ExitStatus) ) {
  803. if ( ExitStatus ) {
  804. printf("Error 0x%lx\n", ExitStatus);
  805. } else {
  806. printf("OK\n");
  807. }
  808. }
  809. return EXIT_SUCCESS;
  810. }
  811. BOOLEAN
  812. UpdateProcessList(PCHAR ProcName,
  813. PCHAR PatchFile)
  814. {
  815. DWORD CrtSize, cbNeeded, cProcesses;
  816. PDWORD aProcesses;
  817. DWORD i;
  818. CrtSize = cbNeeded = 1 * sizeof(DWORD);
  819. aProcesses = malloc(CrtSize);
  820. if ( aProcesses == NULL ) {
  821. exit(EXIT_FAILURE);
  822. }
  823. for ( ;; ) {
  824. if ( !EnumProcesses( aProcesses, CrtSize, &cbNeeded ) )
  825. return FALSE;
  826. if ( CrtSize > cbNeeded ) {
  827. break;
  828. }
  829. free(aProcesses);
  830. CrtSize = cbNeeded + 1024;
  831. aProcesses = malloc(CrtSize);
  832. if ( aProcesses == NULL ) {
  833. exit(EXIT_FAILURE);
  834. }
  835. }
  836. // Calculate how many process identifiers were returned.
  837. cProcesses = cbNeeded / sizeof(DWORD);
  838. // Print the name and process identifier for each process.
  839. for ( i = 0; i < cProcesses; i++ ) {
  840. char szProcessName[MAX_PATH] = "";
  841. DWORD processID = aProcesses[i];
  842. // Get a handle to the process.
  843. HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
  844. PROCESS_VM_READ,
  845. FALSE, processID );
  846. // Get the process name.
  847. if ( hProcess ) {
  848. HMODULE hMod;
  849. DWORD cbNeeded;
  850. if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) ) {
  851. GetModuleBaseName( hProcess, hMod, szProcessName,
  852. sizeof(szProcessName) );
  853. if ( (ProcName == NULL)
  854. ||
  855. _stricmp(szProcessName, ProcName) == 0 ) {
  856. printf("Patching %s : ", szProcessName);
  857. ApplyPatchToProcess(processID, PatchFile);
  858. }
  859. }
  860. CloseHandle( hProcess );
  861. }
  862. }
  863. return TRUE;
  864. }
  865. int __cdecl main (int argc, char ** argv)
  866. {
  867. LONG i;
  868. DWORD id;
  869. DWORD PID;
  870. NTSTATUS Status;
  871. OBJECT_ATTRIBUTES ObjectAttributes;
  872. PCHAR PatchFile = NULL;
  873. BOOLEAN KernelPatch = FALSE;
  874. char * ProgramName = NULL;
  875. //
  876. // By default the tool instals the patch
  877. //
  878. OperationCode = 0;
  879. PID = 0;
  880. for ( i = 1; i < argc; i++ ) {
  881. PCHAR CrtArg = argv[i];
  882. if ( *CrtArg == '-' ) {
  883. CrtArg++;
  884. switch ( toupper(*CrtArg) ) {
  885. case 'I':
  886. if ( OperationCode != 0 ) {
  887. printf("Invalid argument %s\n", CrtArg);
  888. exit(0);
  889. }
  890. OperationCode = PATCH_OC_INSTALL;
  891. break;
  892. case 'U':
  893. if ( OperationCode != 0 ) {
  894. printf("Invalid argument %s\n", CrtArg);
  895. exit(0);
  896. }
  897. OperationCode = PATCH_OC_UNINSTALL;
  898. break;
  899. case 'K':
  900. KernelPatch = TRUE;
  901. break;
  902. case 'R':
  903. if ( OperationCode != 0 ) {
  904. printf("Invalid argument %s\n", CrtArg);
  905. exit(0);
  906. }
  907. OperationCode = PATCH_OC_REPLACE_FILE;
  908. break;
  909. default:
  910. Usage();
  911. return EXIT_FAILURE;
  912. }
  913. } else {
  914. if ( KernelPatch ) {
  915. PatchFile = CrtArg;
  916. } else {
  917. if ( PatchFile == NULL ) {
  918. PatchFile = CrtArg;
  919. } else {
  920. sscanf(CrtArg, "%ld", &PID);
  921. if ( !PID ) {
  922. ProgramName = CrtArg;
  923. //printf("Program %s\n", CrtArg);
  924. }
  925. }
  926. }
  927. }
  928. }
  929. if (OperationCode == 0) {
  930. Usage();
  931. return EXIT_FAILURE;
  932. }
  933. if ( OperationCode == PATCH_OC_REPLACE_FILE ) {
  934. //
  935. // Replace a binary file to a target path
  936. //
  937. HANDLE SfcLibrary;
  938. if ( argc <= 4 ) {
  939. Usage();
  940. return EXIT_FAILURE;
  941. }
  942. SfcLibrary = LoadSfcLibrary();
  943. if ( SfcLibrary ) {
  944. UNICODE_STRING p1, p2, p3;
  945. PSTRToUnicodeString(&p1, argv[2]);
  946. PSTRToUnicodeString(&p2, argv[3]);
  947. PSTRToUnicodeString(&p3, argv[4]);
  948. ReplaceSystemFile(p1.Buffer, p2.Buffer, p3.Buffer);
  949. FreeLibrary(SfcLibrary);
  950. if ( _stricmp(argv[3], "ntdll.dll") == 0 ) {
  951. SYSTEM_HOTPATCH_CODE_INFORMATION KernelPatch;
  952. NTSTATUS Status;
  953. printf("Replacing system ntdll.dll section\n");
  954. if ( !InitializeAsDebugger() ) {
  955. printf("Cannot initialize as debugger\n");
  956. return EXIT_FAILURE;
  957. }
  958. KernelPatch.Flags = FLG_HOTPATCH_RELOAD_NTDLL;
  959. Status = NtSetSystemInformation( SystemHotpatchInformation,
  960. (PVOID)&KernelPatch,
  961. sizeof(KernelPatch) + 100
  962. );
  963. if ( !NT_SUCCESS(Status) ) {
  964. printf("SystemHotpatchInformation failed with %08lx\n", Status);
  965. }
  966. }
  967. }
  968. } else {
  969. if ( !InitializeAsDebugger() ) {
  970. printf("Cannot initialize as debugger\n");
  971. return EXIT_FAILURE;
  972. }
  973. if ( KernelPatch ) {
  974. PSYSTEM_HOTPATCH_CODE_INFORMATION KernelPatchData =
  975. InitializeKernelPatchData( PatchFile,
  976. ((OperationCode == PATCH_OC_INSTALL) ? 1 : 0)
  977. );
  978. if ( KernelPatchData == NULL ) {
  979. return EXIT_FAILURE;
  980. }
  981. KernelPatchData->Flags |= FLG_HOTPATCH_KERNEL;
  982. Status = NtSetSystemInformation( SystemHotpatchInformation,
  983. (PVOID)KernelPatchData,
  984. KernelPatchData->InfoSize
  985. );
  986. free(KernelPatchData);
  987. if ( !NT_SUCCESS(Status) ) {
  988. printf("Patching kernel driver failed with status 0x%lx\n", Status);
  989. return EXIT_FAILURE;
  990. }
  991. return EXIT_SUCCESS;
  992. } else {
  993. //
  994. // Use-mode patching.
  995. //
  996. if ( PID != 0 ) {
  997. return ApplyPatchToProcess(PID, PatchFile);
  998. }
  999. return UpdateProcessList( ProgramName,
  1000. PatchFile);
  1001. }
  1002. }
  1003. return EXIT_SUCCESS;
  1004. }