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.

523 lines
16 KiB

  1. //+-------------------------------------------------------------------------
  2. // Microsoft Windows
  3. //
  4. // Copyright (C) Microsoft Corporation, 2001 - 2001
  5. //
  6. // File: imagehack.cpp
  7. //
  8. // Contents: "Hacked" version of the imagehlp APIs
  9. //
  10. // Contains a "stripped" down subset of the imagehlp functionality
  11. // necessary to hash a PE file and to extract the
  12. // PKCS #7 Signed Data message.
  13. //
  14. // Most of this file is derived from the following 2 files:
  15. // \nt\ds\security\cryptoapi\pkitrust\mssip32\peimage2.cpp
  16. // \nt\sdktools\debuggers\imagehlp\dice.cxx
  17. //
  18. // Functions: imagehack_ImageGetDigestStream
  19. // imagehack_ImageGetCertificateData
  20. //
  21. // History: 20-Jan-01 philh created
  22. //--------------------------------------------------------------------------
  23. #include "global.hxx"
  24. //+=========================================================================
  25. // The following was taken from the following file:
  26. // \nt\ds\security\cryptoapi\pkitrust\mssip32\peimage2.cpp
  27. //-=========================================================================
  28. __inline DWORD AlignIt (DWORD Value, DWORD Alignment) { return (Value + (Alignment - 1)) & ~(Alignment -1); }
  29. #define InitializeListHead(ListHead) (\
  30. (ListHead)->Flink = (ListHead)->Blink = (ListHead))
  31. BOOL
  32. I_CalculateImagePtrs(
  33. PLOADED_IMAGE LoadedImage
  34. )
  35. {
  36. PIMAGE_DOS_HEADER DosHeader;
  37. BOOL fRC = FALSE;
  38. // Everything is mapped. Now check the image and find nt image headers
  39. __try {
  40. DosHeader = (PIMAGE_DOS_HEADER)LoadedImage->MappedAddress;
  41. if ((DosHeader->e_magic != IMAGE_DOS_SIGNATURE) &&
  42. (DosHeader->e_magic != IMAGE_NT_SIGNATURE)) {
  43. __leave;
  44. }
  45. if (DosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
  46. if (DosHeader->e_lfanew == 0) {
  47. __leave;
  48. }
  49. LoadedImage->FileHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)DosHeader + DosHeader->e_lfanew);
  50. if (
  51. // If IMAGE_NT_HEADERS would extend past the end of file...
  52. (PBYTE)LoadedImage->FileHeader + sizeof(IMAGE_NT_HEADERS) >
  53. (PBYTE)LoadedImage->MappedAddress + LoadedImage->SizeOfImage ||
  54. // ..or if it would begin in, or before the IMAGE_DOS_HEADER...
  55. (PBYTE)LoadedImage->FileHeader <
  56. (PBYTE)LoadedImage->MappedAddress + sizeof(IMAGE_DOS_HEADER) )
  57. {
  58. // ...then e_lfanew is not as expected.
  59. // (Several Win95 files are in this category.)
  60. __leave;
  61. }
  62. } else {
  63. // No DOS header indicates an image built w/o a dos stub
  64. LoadedImage->FileHeader = (PIMAGE_NT_HEADERS)DosHeader;
  65. }
  66. if ( LoadedImage->FileHeader->Signature != IMAGE_NT_SIGNATURE ) {
  67. __leave;
  68. } else {
  69. LoadedImage->fDOSImage = FALSE;
  70. }
  71. // No optional header indicates an object...
  72. if ( !LoadedImage->FileHeader->FileHeader.SizeOfOptionalHeader ) {
  73. __leave;
  74. }
  75. // Check for versions < 2.50
  76. if ( LoadedImage->FileHeader->OptionalHeader.MajorLinkerVersion < 3 &&
  77. LoadedImage->FileHeader->OptionalHeader.MinorLinkerVersion < 5 ) {
  78. __leave;
  79. }
  80. InitializeListHead( &LoadedImage->Links );
  81. LoadedImage->NumberOfSections = LoadedImage->FileHeader->FileHeader.NumberOfSections;
  82. LoadedImage->Sections = IMAGE_FIRST_SECTION(LoadedImage->FileHeader);
  83. fRC = TRUE;
  84. } __except ( EXCEPTION_EXECUTE_HANDLER ) { }
  85. return fRC;
  86. }
  87. BOOL
  88. I_MapIt(
  89. PCRYPT_DATA_BLOB pFileBlob,
  90. PLOADED_IMAGE LoadedImage
  91. )
  92. {
  93. LoadedImage->hFile = INVALID_HANDLE_VALUE;
  94. LoadedImage->MappedAddress = pFileBlob->pbData;
  95. LoadedImage->SizeOfImage = pFileBlob->cbData;
  96. if (!LoadedImage->MappedAddress) {
  97. return (FALSE);
  98. }
  99. if (!I_CalculateImagePtrs(LoadedImage)) {
  100. return(FALSE);
  101. }
  102. return(TRUE);
  103. }
  104. typedef struct _EXCLUDE_RANGE {
  105. PBYTE Offset;
  106. DWORD Size;
  107. struct _EXCLUDE_RANGE *Next;
  108. } EXCLUDE_RANGE;
  109. class EXCLUDE_LIST
  110. {
  111. public:
  112. EXCLUDE_LIST() {
  113. m_Image = NULL;
  114. m_ExRange = (EXCLUDE_RANGE *)I_MemAlloc(sizeof(EXCLUDE_RANGE));
  115. if(m_ExRange)
  116. memset(m_ExRange, 0x00, sizeof(EXCLUDE_RANGE));
  117. }
  118. ~EXCLUDE_LIST() {
  119. EXCLUDE_RANGE *pTmp;
  120. pTmp = m_ExRange->Next;
  121. while (pTmp)
  122. {
  123. I_MemFree(m_ExRange);
  124. m_ExRange = pTmp;
  125. pTmp = m_ExRange->Next;
  126. }
  127. I_MemFree(m_ExRange);
  128. }
  129. void Init(LOADED_IMAGE * Image, DIGEST_FUNCTION pFunc, DIGEST_HANDLE dh) {
  130. m_Image = Image;
  131. m_ExRange->Offset = NULL;
  132. m_ExRange->Size = 0;
  133. m_pFunc = pFunc;
  134. m_dh = dh;
  135. return;
  136. }
  137. void Add(DWORD_PTR Offset, DWORD Size);
  138. BOOL Emit(PBYTE Offset, DWORD Size);
  139. private:
  140. LOADED_IMAGE * m_Image;
  141. EXCLUDE_RANGE * m_ExRange;
  142. DIGEST_FUNCTION m_pFunc;
  143. DIGEST_HANDLE m_dh;
  144. };
  145. void
  146. EXCLUDE_LIST::Add(
  147. DWORD_PTR Offset,
  148. DWORD Size
  149. )
  150. {
  151. EXCLUDE_RANGE *pTmp, *pExRange;
  152. pExRange = m_ExRange;
  153. while (pExRange->Next && (pExRange->Next->Offset < (PBYTE)Offset)) {
  154. pExRange = pExRange->Next;
  155. }
  156. pTmp = (EXCLUDE_RANGE *) I_MemAlloc(sizeof(EXCLUDE_RANGE));
  157. if(pTmp)
  158. {
  159. pTmp->Next = pExRange->Next;
  160. pTmp->Offset = (PBYTE)Offset;
  161. pTmp->Size = Size;
  162. pExRange->Next = pTmp;
  163. }
  164. return;
  165. }
  166. BOOL
  167. EXCLUDE_LIST::Emit(
  168. PBYTE Offset,
  169. DWORD Size
  170. )
  171. {
  172. BOOL rc = FALSE;
  173. EXCLUDE_RANGE *pExRange;
  174. DWORD EmitSize, ExcludeSize;
  175. pExRange = m_ExRange->Next;
  176. while (pExRange && (Size > 0)) {
  177. if (pExRange->Offset >= Offset) {
  178. // Emit what's before the exclude list.
  179. EmitSize = min((DWORD)(pExRange->Offset - Offset), Size);
  180. if (EmitSize) {
  181. rc = (*m_pFunc)(m_dh, Offset, EmitSize);
  182. Size -= EmitSize;
  183. Offset += EmitSize;
  184. }
  185. }
  186. if (Size) {
  187. if (pExRange->Offset + pExRange->Size >= Offset) {
  188. // Skip over what's in the exclude list.
  189. ExcludeSize = min(Size, (DWORD)(pExRange->Offset + pExRange->Size - Offset));
  190. Size -= ExcludeSize;
  191. Offset += ExcludeSize;
  192. }
  193. }
  194. pExRange = pExRange->Next;
  195. }
  196. // Emit what's left.
  197. if (Size) {
  198. rc = (*m_pFunc)(m_dh, Offset, Size);
  199. }
  200. return rc;
  201. }
  202. #pragma warning (push)
  203. // error C4509: nonstandard extension used: 'imagehack_ImageGetDigestStream'
  204. // uses SEH and 'ExList' has destructor
  205. #pragma warning (disable: 4509)
  206. BOOL
  207. WINAPI
  208. imagehack_ImageGetDigestStream(
  209. IN PCRYPT_DATA_BLOB pFileBlob,
  210. IN DWORD DigestLevel,
  211. IN DIGEST_FUNCTION DigestFunction,
  212. IN DIGEST_HANDLE DigestHandle
  213. )
  214. /*++
  215. Routine Description:
  216. Given an image, return the bytes necessary to construct a certificate.
  217. Only PE images are supported at this time.
  218. Arguments:
  219. FileHandle - Handle to the file in question. The file should be opened
  220. with at least GENERIC_READ access.
  221. DigestLevel - Indicates what data will be included in the returned buffer.
  222. Valid values are:
  223. CERT_PE_IMAGE_DIGEST_ALL_BUT_CERTS - Include data outside the PE image itself
  224. (may include non-mapped debug symbolic)
  225. DigestFunction - User supplied routine that will process the data.
  226. DigestHandle - User supplied handle to identify the digest. Passed as the first
  227. argument to the DigestFunction.
  228. Return Value:
  229. TRUE - Success.
  230. FALSE - There was some error. Call GetLastError for more information. Possible
  231. values are ERROR_INVALID_PARAMETER or ERROR_OPERATION_ABORTED.
  232. --*/
  233. {
  234. LOADED_IMAGE LoadedImage;
  235. DWORD ErrorCode;
  236. EXCLUDE_LIST ExList;
  237. if (I_MapIt(pFileBlob, &LoadedImage) == FALSE) {
  238. // Unable to map the image or invalid digest level.
  239. SetLastError(ERROR_INVALID_PARAMETER);
  240. return(FALSE);
  241. }
  242. ErrorCode = ERROR_INVALID_PARAMETER;
  243. __try {
  244. PIMAGE_DATA_DIRECTORY CertDirectory;
  245. DWORD HeaderEndOffset = 0;
  246. if ((LoadedImage.FileHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) &&
  247. (LoadedImage.FileHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC))
  248. {
  249. __leave;
  250. }
  251. ExList.Init(&LoadedImage, DigestFunction, DigestHandle);
  252. if (LoadedImage.FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
  253. PIMAGE_NT_HEADERS32 NtHeader32 = (PIMAGE_NT_HEADERS32)(LoadedImage.FileHeader);
  254. // Exclude the checksum.
  255. ExList.Add(((DWORD_PTR) &NtHeader32->OptionalHeader.CheckSum),
  256. sizeof(NtHeader32->OptionalHeader.CheckSum));
  257. CertDirectory = &NtHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
  258. HeaderEndOffset = NtHeader32->OptionalHeader.SizeOfHeaders;
  259. } else {
  260. PIMAGE_NT_HEADERS64 NtHeader64 = (PIMAGE_NT_HEADERS64)(LoadedImage.FileHeader);
  261. // Exclude the checksum.
  262. ExList.Add(((DWORD_PTR) &NtHeader64->OptionalHeader.CheckSum),
  263. sizeof(NtHeader64->OptionalHeader.CheckSum));
  264. CertDirectory = &NtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
  265. HeaderEndOffset = NtHeader64->OptionalHeader.SizeOfHeaders;
  266. }
  267. DWORD CertFileOffset = CertDirectory->VirtualAddress;
  268. DWORD CertFileSize = CertDirectory->Size;
  269. if (CertFileOffset && CertFileSize) {
  270. DWORD i;
  271. if (CertFileOffset > LoadedImage.SizeOfImage) {
  272. __leave; // Start of certs is past end of image
  273. }
  274. if ((CertFileOffset + CertFileSize) != LoadedImage.SizeOfImage) {
  275. __leave; // Certs not at end of image
  276. }
  277. if ((CertFileOffset + CertFileSize) < CertFileOffset) {
  278. __leave; // cert end is before cert start (start + size wraps)
  279. }
  280. if (CertFileOffset < HeaderEndOffset) {
  281. __leave; // Certs are in the header space
  282. }
  283. // See if the certs are in the section data
  284. for (i = 0; i < LoadedImage.NumberOfSections; i++) {
  285. DWORD SectionFileOffsetStart = LoadedImage.Sections[i].PointerToRawData;
  286. DWORD SectionFileOffsetEnd = SectionFileOffsetStart + LoadedImage.Sections[i].SizeOfRawData;
  287. if (SectionFileOffsetStart && (CertFileOffset < SectionFileOffsetEnd)) {
  288. __leave; // CertData starts before this section - not allowed
  289. }
  290. }
  291. }
  292. // Exclude the Security directory.
  293. ExList.Add((DWORD_PTR) CertDirectory, sizeof(IMAGE_DATA_DIRECTORY));
  294. // Exclude the certs.
  295. ExList.Add((DWORD_PTR)CertFileOffset + (DWORD_PTR)LoadedImage.MappedAddress, CertFileSize);
  296. ExList.Emit((PBYTE) (LoadedImage.MappedAddress), LoadedImage.SizeOfImage);
  297. ErrorCode = ERROR_SUCCESS;
  298. } __except(EXCEPTION_EXECUTE_HANDLER) { }
  299. SetLastError(ErrorCode);
  300. return(ErrorCode == ERROR_SUCCESS ? TRUE : FALSE);
  301. }
  302. #pragma warning (pop)
  303. //+=========================================================================
  304. // The following was taken from the following file:
  305. // \nt\sdktools\debuggers\imagehlp\dice.cxx
  306. //-=========================================================================
  307. BOOL
  308. I_FindCertificate(
  309. IN PLOADED_IMAGE LoadedImage,
  310. IN DWORD Index,
  311. OUT LPWIN_CERTIFICATE * Certificate
  312. )
  313. {
  314. PIMAGE_DATA_DIRECTORY pDataDir;
  315. DWORD_PTR CurrentCert = NULL;
  316. BOOL rc;
  317. if (LoadedImage->fDOSImage) {
  318. // No way this could have a certificate;
  319. return(FALSE);
  320. }
  321. rc = FALSE;
  322. __try {
  323. if (LoadedImage->FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
  324. pDataDir = &((PIMAGE_NT_HEADERS32)(LoadedImage->FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
  325. } else if (LoadedImage->FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
  326. pDataDir = &((PIMAGE_NT_HEADERS64)(LoadedImage->FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
  327. } else {
  328. __leave; // Not an interesting file type.
  329. }
  330. // Check if the cert pointer is at least reasonable.
  331. if (!pDataDir->VirtualAddress ||
  332. !pDataDir->Size ||
  333. (pDataDir->VirtualAddress + pDataDir->Size > LoadedImage->SizeOfImage))
  334. {
  335. __leave;
  336. }
  337. // We're not looking at an empty security slot or an invalid (past the image boundary) value.
  338. // Let's see if we can find it.
  339. DWORD CurrentIdx = 0;
  340. DWORD_PTR LastCert;
  341. CurrentCert = (DWORD_PTR)(LoadedImage->MappedAddress) + pDataDir->VirtualAddress;
  342. LastCert = CurrentCert + pDataDir->Size;
  343. while (CurrentCert < LastCert ) {
  344. if (CurrentIdx == Index) {
  345. rc = TRUE;
  346. __leave;
  347. }
  348. CurrentIdx++;
  349. CurrentCert += ((LPWIN_CERTIFICATE)CurrentCert)->dwLength;
  350. CurrentCert = (CurrentCert + 7) & ~7; // align it.
  351. }
  352. } __except(EXCEPTION_EXECUTE_HANDLER) { }
  353. if (rc == TRUE) {
  354. *Certificate = (LPWIN_CERTIFICATE)CurrentCert;
  355. }
  356. return(rc);
  357. }
  358. BOOL
  359. WINAPI
  360. imagehack_ImageGetCertificateData(
  361. IN PCRYPT_DATA_BLOB pFileBlob,
  362. IN DWORD CertificateIndex,
  363. OUT LPWIN_CERTIFICATE * Certificate
  364. )
  365. /*++
  366. Routine Description:
  367. Given a specific certificate index, retrieve the certificate data.
  368. Arguments:
  369. FileHandle - Handle to the file in question. The file should be opened
  370. with at least GENERIC_READ access.
  371. CertificateIndex - Index to retrieve
  372. Certificate - Output buffer where the certificate is to be stored.
  373. RequiredLength - Size of the certificate buffer (input). On return, is
  374. set to the actual certificate length. NULL can be used
  375. to determine the size of a certificate.
  376. Return Value:
  377. TRUE - Successful
  378. FALSE - There was some error. Call GetLastError() for more information.
  379. --*/
  380. {
  381. LOADED_IMAGE LoadedImage;
  382. DWORD ErrorCode;
  383. LPWIN_CERTIFICATE ImageCert;
  384. *Certificate = NULL;
  385. // if (I_MapIt(FileHandle, &LoadedImage, MAP_READONLY) == FALSE) {
  386. if (I_MapIt(pFileBlob, &LoadedImage) == FALSE) {
  387. // Unable to map the image.
  388. SetLastError(ERROR_INVALID_PARAMETER);
  389. return(FALSE);
  390. }
  391. ErrorCode = ERROR_INVALID_PARAMETER;
  392. __try {
  393. if (I_FindCertificate(&LoadedImage, CertificateIndex, &ImageCert) == FALSE) {
  394. __leave;
  395. }
  396. *Certificate = ImageCert;
  397. ErrorCode = ERROR_SUCCESS;
  398. } __except(EXCEPTION_EXECUTE_HANDLER) { }
  399. // I_UnMapIt(&LoadedImage);
  400. SetLastError(ErrorCode);
  401. return(ErrorCode == ERROR_SUCCESS ? TRUE: FALSE);
  402. }