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

788 lines
17 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. // FILE : selfmac.c //
  3. // DESCRIPTION : Code to do self MACing //
  4. // AUTHOR : //
  5. // HISTORY : //
  6. // Nov 04 1999 jeffspel Added provider type checking //
  7. // Mar 2000 kschutz Added stuff to make it work in kernel //
  8. // //
  9. // Copyright (C) 1999 Microsoft Corporation All Rights Reserved //
  10. /////////////////////////////////////////////////////////////////////////////
  11. #include <nt.h>
  12. #include <ntrtl.h>
  13. #include <nturtl.h>
  14. #include <zwapi.h>
  15. #include <windows.h>
  16. #ifdef KERNEL_MODE
  17. #include <ntosp.h>
  18. #else
  19. #include <imagehlp.h>
  20. #endif // KERNEL_MODE
  21. #include <des.h>
  22. #include <modes.h>
  23. // MAC in file
  24. typedef struct _MAC_STRUCT
  25. {
  26. ULONG CoolMac[2];
  27. ULONG dwMACStructOffset;
  28. UCHAR rgbMac[DES_BLOCKLEN];
  29. ULONG dwImageCheckSumOffset;
  30. } MAC_STRUCT;
  31. #define MAC_STRING "COOL MAC "
  32. static LPSTR g_pszMAC = MAC_STRING;
  33. static MAC_STRUCT *g_pMACStruct;
  34. // The function MACs the given bytes.
  35. VOID
  36. MACBytes(
  37. IN DESTable *pDESKeyTable,
  38. IN UCHAR *pbData,
  39. IN ULONG cbData,
  40. IN OUT UCHAR *pbTmp,
  41. IN OUT ULONG *pcbTmp,
  42. IN OUT UCHAR *pbMAC,
  43. IN BOOLEAN fFinal
  44. )
  45. {
  46. ULONG cb = cbData;
  47. ULONG cbMACed = 0;
  48. while (cb)
  49. {
  50. if ((cb + *pcbTmp) < DES_BLOCKLEN)
  51. {
  52. memcpy(pbTmp + *pcbTmp, pbData + cbMACed, cb);
  53. *pcbTmp += cb;
  54. break;
  55. }
  56. else
  57. {
  58. memcpy(pbTmp + *pcbTmp, pbData + cbMACed, DES_BLOCKLEN - *pcbTmp);
  59. CBC(des, DES_BLOCKLEN, pbMAC, pbTmp, pDESKeyTable,
  60. ENCRYPT, pbMAC);
  61. cbMACed = cbMACed + (DES_BLOCKLEN - *pcbTmp);
  62. cb = cb - (DES_BLOCKLEN - *pcbTmp);
  63. *pcbTmp = 0;
  64. }
  65. }
  66. }
  67. #define CSP_TO_BE_MACED_CHUNK 512
  68. // Given hFile, reads the specified number of bytes (cbToBeMACed) from the file
  69. // and MACs these bytes. The function does this in chunks.
  70. NTSTATUS
  71. MACBytesOfFile(
  72. IN HANDLE hFile,
  73. IN ULONG cbToBeMACed,
  74. IN DESTable *pDESKeyTable,
  75. IN UCHAR *pbTmp,
  76. IN ULONG *pcbTmp,
  77. IN UCHAR *pbMAC,
  78. IN BOOLEAN fNoMacing,
  79. IN BOOLEAN fFinal
  80. )
  81. {
  82. UCHAR rgbChunk[CSP_TO_BE_MACED_CHUNK];
  83. ULONG cbRemaining = cbToBeMACed, cbToRead, cbBytesRead;
  84. NTSTATUS Status = STATUS_SUCCESS;
  85. #ifdef KERNEL_MODE
  86. IO_STATUS_BLOCK IoStatusBlock;
  87. #endif // END KERNEL/USER MODE CHECK
  88. //
  89. // loop over the file for the specified number of bytes
  90. // updating the hash as we go.
  91. //
  92. while (cbRemaining > 0)
  93. {
  94. if (cbRemaining < CSP_TO_BE_MACED_CHUNK)
  95. cbToRead = cbRemaining;
  96. else
  97. cbToRead = CSP_TO_BE_MACED_CHUNK;
  98. #ifdef KERNEL_MODE
  99. Status = ZwReadFile(hFile,
  100. NULL,
  101. NULL,
  102. NULL,
  103. &IoStatusBlock,
  104. rgbChunk,
  105. cbToRead,
  106. NULL,
  107. NULL);
  108. if (!NT_SUCCESS(Status))
  109. {
  110. goto Ret;
  111. }
  112. if (cbToRead != IoStatusBlock.Information)
  113. {
  114. Status = STATUS_UNSUCCESSFUL;
  115. goto Ret;
  116. }
  117. cbBytesRead = cbToRead;
  118. #else // USER MODE
  119. if(!ReadFile(hFile,
  120. rgbChunk,
  121. cbToRead,
  122. &cbBytesRead,
  123. NULL))
  124. {
  125. Status = STATUS_UNSUCCESSFUL;
  126. goto Ret;
  127. }
  128. if (cbBytesRead != cbToRead)
  129. {
  130. Status = STATUS_UNSUCCESSFUL;
  131. goto Ret;
  132. }
  133. #endif // END KERNEL/USER MODE CHECK
  134. if (!fNoMacing)
  135. {
  136. MACBytes(pDESKeyTable,
  137. rgbChunk,
  138. cbBytesRead,
  139. pbTmp,
  140. pcbTmp,
  141. pbMAC,
  142. fFinal);
  143. }
  144. cbRemaining -= cbToRead;
  145. }
  146. Ret:
  147. return Status;
  148. }
  149. static UCHAR rgbMACDESKey[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
  150. NTSTATUS
  151. MACTheFile(
  152. LPCWSTR pszImage,
  153. ULONG cbImageCheckSumOffset,
  154. ULONG cbMACStructOffset,
  155. UCHAR *pbMAC
  156. )
  157. {
  158. ULONG cbFileLen = 0,cbHighPart, cbBytesToMac;
  159. HANDLE hFile = INVALID_HANDLE_VALUE;
  160. DESTable DESKeyTable;
  161. BYTE rgbTmp[DES_BLOCKLEN];
  162. DWORD cbTmp = 0;
  163. MAC_STRUCT TmpMacStruct;
  164. NTSTATUS Status = STATUS_SUCCESS;
  165. #ifdef KERNEL_MODE
  166. UNICODE_STRING ObjectName;
  167. OBJECT_ATTRIBUTES ObjectAttribs;
  168. IO_STATUS_BLOCK IoStatusBlock;
  169. BOOLEAN fFileOpened = FALSE;
  170. FILE_STANDARD_INFORMATION FileInformation;
  171. #endif // END KERNEL/USER MODE CHECK
  172. RtlZeroMemory(pbMAC, DES_BLOCKLEN);
  173. RtlZeroMemory(rgbTmp, sizeof(rgbTmp));
  174. RtlZeroMemory(&TmpMacStruct, sizeof(TmpMacStruct));
  175. #ifdef KERNEL_MODE
  176. //
  177. // get file length - kernel mode version
  178. //
  179. RtlZeroMemory(&ObjectAttribs, sizeof(ObjectAttribs));
  180. RtlInitUnicodeString( &ObjectName, pszImage );
  181. InitializeObjectAttributes(
  182. &ObjectAttribs,
  183. &ObjectName,
  184. OBJ_CASE_INSENSITIVE,
  185. NULL,
  186. NULL
  187. );
  188. Status = ZwCreateFile(
  189. &hFile,
  190. SYNCHRONIZE | FILE_READ_DATA,
  191. &ObjectAttribs,
  192. &IoStatusBlock,
  193. NULL,
  194. FILE_ATTRIBUTE_NORMAL,
  195. FILE_SHARE_READ ,
  196. FILE_OPEN,
  197. FILE_SYNCHRONOUS_IO_NONALERT,
  198. NULL,
  199. 0
  200. );
  201. if (!NT_SUCCESS(Status))
  202. {
  203. goto Ret;
  204. }
  205. fFileOpened = TRUE;
  206. Status = ZwQueryInformationFile(
  207. hFile,
  208. &IoStatusBlock,
  209. &FileInformation,
  210. sizeof(FILE_STANDARD_INFORMATION),
  211. FileStandardInformation
  212. );
  213. if (!NT_SUCCESS(Status))
  214. {
  215. goto Ret;
  216. }
  217. cbFileLen = FileInformation.EndOfFile.LowPart;
  218. #else // USER MODE
  219. //
  220. // get file length - user mode version
  221. //
  222. if ((hFile = CreateFileW(
  223. pszImage,
  224. GENERIC_READ,
  225. FILE_SHARE_READ,
  226. NULL,
  227. OPEN_EXISTING,
  228. FILE_ATTRIBUTE_NORMAL,
  229. NULL)) == INVALID_HANDLE_VALUE)
  230. {
  231. Status = STATUS_UNSUCCESSFUL;
  232. goto Ret;
  233. }
  234. cbFileLen = GetFileSize(hFile, &cbHighPart);
  235. #endif // END KERNEL/USER MODE CHECK
  236. if (cbFileLen < sizeof(MAC_STRUCT))
  237. {
  238. Status = STATUS_UNSUCCESSFUL;
  239. goto Ret;
  240. }
  241. // init the key table
  242. deskey(&DESKeyTable, rgbMACDESKey);
  243. // MAC the file the following way:
  244. // - MAC from start to image check sum
  245. // - skip over the image check sum
  246. // - mac from after the image check sum to the mac struct
  247. // - skip over the mac struct
  248. // - mac the rest of the file
  249. // MAC from the start to the image check sum offset
  250. Status = MACBytesOfFile(
  251. hFile,
  252. cbImageCheckSumOffset,
  253. &DESKeyTable,
  254. rgbTmp,
  255. &cbTmp,
  256. pbMAC,
  257. FALSE,
  258. FALSE
  259. );
  260. if (!NT_SUCCESS(Status))
  261. {
  262. goto Ret;
  263. }
  264. // Skip over the image checksum
  265. Status = MACBytesOfFile(
  266. hFile,
  267. sizeof(DWORD),
  268. &DESKeyTable,
  269. rgbTmp,
  270. &cbTmp,
  271. pbMAC,
  272. TRUE,
  273. FALSE
  274. );
  275. if (!NT_SUCCESS(Status))
  276. {
  277. goto Ret;
  278. }
  279. // MAC from after the image checksum to the MAC struct offset
  280. cbBytesToMac = cbMACStructOffset - sizeof(DWORD) - cbImageCheckSumOffset;
  281. Status = MACBytesOfFile(
  282. hFile,
  283. cbBytesToMac,
  284. &DESKeyTable,
  285. rgbTmp,
  286. &cbTmp,
  287. pbMAC,
  288. FALSE,
  289. FALSE
  290. );
  291. if (!NT_SUCCESS(Status))
  292. {
  293. goto Ret;
  294. }
  295. // skip over the mac struct
  296. Status = MACBytesOfFile(
  297. hFile,
  298. sizeof(MAC_STRUCT),
  299. &DESKeyTable,
  300. rgbTmp,
  301. &cbTmp,
  302. pbMAC,
  303. TRUE,
  304. FALSE
  305. );
  306. if (!NT_SUCCESS(Status))
  307. {
  308. goto Ret;
  309. }
  310. // MAC data after the MAC struct
  311. cbBytesToMac = cbFileLen - cbMACStructOffset - sizeof(MAC_STRUCT);
  312. Status = MACBytesOfFile(
  313. hFile,
  314. cbBytesToMac,
  315. &DESKeyTable,
  316. rgbTmp,
  317. &cbTmp,
  318. pbMAC,
  319. FALSE,
  320. TRUE
  321. );
  322. if (!NT_SUCCESS(Status))
  323. {
  324. goto Ret;
  325. }
  326. Ret:
  327. #ifdef KERNEL_MODE
  328. if (fFileOpened)
  329. {
  330. ZwClose(hFile);
  331. }
  332. #else
  333. if (INVALID_HANDLE_VALUE != hFile)
  334. {
  335. CloseHandle(hFile);
  336. }
  337. #endif
  338. return Status;
  339. }
  340. // **********************************************************************
  341. // SelfMACCheck performs a DES MAC on the binary image of this DLL
  342. // **********************************************************************
  343. NTSTATUS
  344. SelfMACCheck(
  345. IN LPWSTR pszImage
  346. )
  347. {
  348. UCHAR rgbMac[DES_BLOCKLEN];
  349. NTSTATUS Status = STATUS_SUCCESS;
  350. g_pMACStruct = (MAC_STRUCT*) g_pszMAC;
  351. Status = MACTheFile(
  352. pszImage,
  353. g_pMACStruct->dwImageCheckSumOffset,
  354. g_pMACStruct->dwMACStructOffset,
  355. rgbMac
  356. );
  357. if (!NT_SUCCESS(Status))
  358. {
  359. goto Ret;
  360. }
  361. if (RtlCompareMemory(
  362. rgbMac,
  363. g_pMACStruct->rgbMac,
  364. sizeof(rgbMac)) != sizeof(rgbMac))
  365. {
  366. Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
  367. goto Ret;
  368. }
  369. Ret:
  370. return Status;
  371. }
  372. #ifndef KERNEL_MODE
  373. //
  374. // Find the offset to the MAC structure
  375. //
  376. NTSTATUS
  377. FindTheMACStructOffset(
  378. LPWSTR pszImage,
  379. ULONG *pcbMACStructOffset
  380. )
  381. {
  382. HANDLE hFile = INVALID_HANDLE_VALUE;
  383. ULONG cbRemaining, cbBytesRead, cbHighPart, cbFileLen, cbCompare = 0;
  384. UCHAR b;
  385. NTSTATUS Status = STATUS_SUCCESS;
  386. *pcbMACStructOffset = 0;
  387. // Load the file
  388. if ((hFile = CreateFileW(
  389. pszImage,
  390. GENERIC_READ,
  391. FILE_SHARE_READ,
  392. NULL,
  393. OPEN_EXISTING,
  394. FILE_ATTRIBUTE_NORMAL,
  395. NULL)) == INVALID_HANDLE_VALUE)
  396. {
  397. Status = STATUS_UNSUCCESSFUL;
  398. goto Ret;
  399. }
  400. cbFileLen = GetFileSize(hFile, &cbHighPart);
  401. cbRemaining = cbFileLen;
  402. // read file to the correct location
  403. while (cbRemaining > 0)
  404. {
  405. if(!ReadFile(hFile,
  406. &b,
  407. 1,
  408. &cbBytesRead,
  409. NULL))
  410. {
  411. Status = STATUS_UNSUCCESSFUL;
  412. goto Ret;
  413. }
  414. if (cbBytesRead != 1)
  415. {
  416. Status = STATUS_UNSUCCESSFUL;
  417. goto Ret;
  418. }
  419. if (b == g_pszMAC[cbCompare])
  420. {
  421. cbCompare++;
  422. if (cbCompare == 8)
  423. {
  424. *pcbMACStructOffset = (cbFileLen - (cbRemaining + 7)) ;
  425. break;
  426. }
  427. }
  428. else
  429. {
  430. cbCompare = 0;
  431. }
  432. cbRemaining--;
  433. }
  434. if (cbCompare != 8)
  435. {
  436. Status = STATUS_UNSUCCESSFUL;
  437. goto Ret;
  438. }
  439. Ret:
  440. if (INVALID_HANDLE_VALUE != hFile)
  441. {
  442. CloseHandle(hFile);
  443. }
  444. return Status;
  445. }
  446. NTSTATUS
  447. GetImageCheckSumOffset(
  448. LPWSTR pszImage,
  449. ULONG *pcbImageCheckSumOffset
  450. )
  451. {
  452. HANDLE hFile = INVALID_HANDLE_VALUE;
  453. HANDLE hFileMap = INVALID_HANDLE_VALUE;
  454. ULONG cbHighPart;
  455. ULONG cbFileLen;
  456. PBYTE pbFilePtr = NULL;
  457. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  458. PIMAGE_NT_HEADERS pImageNTHdrs;
  459. DWORD OldCheckSum;
  460. DWORD NewCheckSum;
  461. if (INVALID_HANDLE_VALUE == (hFile = CreateFileW(
  462. pszImage,
  463. GENERIC_READ | GENERIC_WRITE,
  464. FILE_SHARE_READ,
  465. NULL,
  466. OPEN_EXISTING,
  467. FILE_ATTRIBUTE_NORMAL,
  468. NULL)))
  469. {
  470. goto Ret;
  471. }
  472. // make sure the file is larger than the indicated offset
  473. cbFileLen = GetFileSize(hFile, &cbHighPart);
  474. // map the file to memory
  475. if (NULL == (hFileMap = CreateFileMapping(
  476. hFile,
  477. NULL,
  478. PAGE_READWRITE,
  479. 0,
  480. 0,
  481. NULL)))
  482. {
  483. goto Ret;
  484. }
  485. // get a memory view of the file
  486. if (NULL == (pbFilePtr = (PBYTE) MapViewOfFile(
  487. hFileMap,
  488. FILE_MAP_ALL_ACCESS,
  489. 0,
  490. 0,
  491. 0)))
  492. {
  493. goto Ret;
  494. }
  495. // get the pointer to the image checksum
  496. if (NULL == (pImageNTHdrs = CheckSumMappedFile(
  497. pbFilePtr, cbFileLen,
  498. &OldCheckSum, &NewCheckSum))) {
  499. goto Ret;
  500. }
  501. *pcbImageCheckSumOffset =
  502. (ULONG) ((PBYTE) &pImageNTHdrs->OptionalHeader.CheckSum - pbFilePtr);
  503. Status = STATUS_SUCCESS;
  504. Ret:
  505. if (pbFilePtr) {
  506. UnmapViewOfFile(pbFilePtr);
  507. }
  508. if (INVALID_HANDLE_VALUE != hFileMap) {
  509. CloseHandle(hFileMap);
  510. }
  511. if (INVALID_HANDLE_VALUE != hFile)
  512. {
  513. CloseHandle(hFile);
  514. }
  515. return Status;
  516. }
  517. // write the MAC information into the MAC struct in the file
  518. NTSTATUS
  519. WriteMACToTheFile(
  520. LPWSTR pszImage,
  521. MAC_STRUCT *pMacStructOriginal,
  522. ULONG cbMACStructOffset,
  523. UCHAR *pbMac
  524. )
  525. {
  526. HANDLE hFile = INVALID_HANDLE_VALUE;
  527. HANDLE hFileMap = INVALID_HANDLE_VALUE;
  528. PBYTE pbFilePtr = NULL;
  529. MAC_STRUCT TmpMacStruct;
  530. ULONG cbWritten = 0, cbRemaining = cbMACStructOffset;
  531. ULONG cbToRead, cbBytesRead, cbHighPart,cbFileLen;
  532. UCHAR rgbChunk[CSP_TO_BE_MACED_CHUNK];
  533. NTSTATUS Status = STATUS_SUCCESS;
  534. DWORD OldCheckSum, NewCheckSum;
  535. PIMAGE_NT_HEADERS pImageNTHdrs;
  536. RtlCopyMemory(&TmpMacStruct, pMacStructOriginal, sizeof(TmpMacStruct));
  537. // Load the file
  538. if ((hFile = CreateFileW(
  539. pszImage,
  540. GENERIC_READ | GENERIC_WRITE,
  541. FILE_SHARE_READ,
  542. NULL,
  543. OPEN_EXISTING,
  544. FILE_ATTRIBUTE_NORMAL,
  545. NULL)) == INVALID_HANDLE_VALUE)
  546. {
  547. Status = STATUS_UNSUCCESSFUL;
  548. goto Ret;
  549. }
  550. // make sure the file is larger than the indicated offset
  551. cbFileLen = GetFileSize(hFile, &cbHighPart);
  552. if (cbFileLen < cbMACStructOffset)
  553. {
  554. Status = STATUS_UNSUCCESSFUL;
  555. goto Ret;
  556. }
  557. // map the file to memory
  558. if ((hFileMap = CreateFileMapping(
  559. hFile,
  560. NULL,
  561. PAGE_READWRITE,
  562. 0,
  563. 0,
  564. NULL)) == NULL)
  565. {
  566. Status = STATUS_UNSUCCESSFUL;
  567. goto Ret;
  568. }
  569. // get a memory view of the file
  570. if ((pbFilePtr = (PBYTE) MapViewOfFile(
  571. hFileMap,
  572. FILE_MAP_ALL_ACCESS,
  573. 0,
  574. 0,
  575. 0)) == NULL)
  576. {
  577. Status = STATUS_UNSUCCESSFUL;
  578. goto Ret;
  579. }
  580. // get the pointer to the image checksum
  581. if (NULL == (pImageNTHdrs = CheckSumMappedFile(
  582. pbFilePtr, cbFileLen,
  583. &OldCheckSum, &NewCheckSum))) {
  584. Status = STATUS_UNSUCCESSFUL;
  585. goto Ret;
  586. }
  587. // set up and write the MAC struct
  588. TmpMacStruct.dwImageCheckSumOffset =
  589. (ULONG) ((PBYTE) &pImageNTHdrs->OptionalHeader.CheckSum - pbFilePtr);
  590. TmpMacStruct.dwMACStructOffset = cbMACStructOffset;
  591. RtlCopyMemory(TmpMacStruct.rgbMac, pbMac, sizeof(TmpMacStruct.rgbMac));
  592. // now copy the new mac struct back to the view
  593. RtlCopyMemory(pbFilePtr + cbMACStructOffset, &TmpMacStruct, sizeof(TmpMacStruct));
  594. // compute a new checksum
  595. if (NULL == (pImageNTHdrs = CheckSumMappedFile(
  596. pbFilePtr, cbFileLen,
  597. &OldCheckSum, &NewCheckSum))) {
  598. Status = STATUS_UNSUCCESSFUL;
  599. goto Ret;
  600. }
  601. // and copy the new checksum back to the header
  602. CopyMemory(&pImageNTHdrs->OptionalHeader.CheckSum, &NewCheckSum, sizeof(DWORD));
  603. Ret:
  604. if (pbFilePtr) {
  605. UnmapViewOfFile(pbFilePtr);
  606. }
  607. if (INVALID_HANDLE_VALUE != hFileMap) {
  608. CloseHandle(hFileMap);
  609. }
  610. if (INVALID_HANDLE_VALUE != hFile)
  611. {
  612. CloseHandle(hFile);
  613. }
  614. return Status;
  615. }
  616. // **********************************************************************
  617. // MACTheBinary performs a MAC on the binary and writes the value into
  618. // the g_pMACStruct
  619. // **********************************************************************
  620. NTSTATUS
  621. MACTheBinary(
  622. IN LPWSTR pszImage
  623. )
  624. {
  625. UCHAR rgbMAC[DES_BLOCKLEN];
  626. ULONG cbMACStructOffset = 0, cbImageCheckSumOffset = 0;
  627. NTSTATUS Status = STATUS_SUCCESS;
  628. g_pMACStruct = (MAC_STRUCT*) g_pszMAC;
  629. // Find the offset to the MAC structure
  630. Status = FindTheMACStructOffset(
  631. pszImage,
  632. &cbMACStructOffset
  633. );
  634. if (!NT_SUCCESS(Status))
  635. {
  636. goto Ret;
  637. }
  638. // Get the offset of the image checksum
  639. Status = GetImageCheckSumOffset(
  640. pszImage,
  641. &cbImageCheckSumOffset
  642. );
  643. if (!NT_SUCCESS(Status))
  644. {
  645. goto Ret;
  646. }
  647. // MAC the file
  648. Status = MACTheFile(
  649. pszImage,
  650. cbImageCheckSumOffset,
  651. cbMACStructOffset,
  652. rgbMAC
  653. );
  654. if (!NT_SUCCESS(Status))
  655. {
  656. goto Ret;
  657. }
  658. // write the MAC information into the MAC struct in the file
  659. Status = WriteMACToTheFile(
  660. pszImage,
  661. g_pMACStruct,
  662. cbMACStructOffset,
  663. rgbMAC
  664. );
  665. if (!NT_SUCCESS(Status))
  666. {
  667. goto Ret;
  668. }
  669. Ret:
  670. return Status;
  671. }
  672. #endif // NOT IN KERNEL_MODE