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.

497 lines
15 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. if ((LoadedImage.FileHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) &&
  245. (LoadedImage.FileHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC))
  246. {
  247. __leave;
  248. }
  249. ExList.Init(&LoadedImage, DigestFunction, DigestHandle);
  250. if (LoadedImage.FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
  251. PIMAGE_NT_HEADERS32 NtHeader32 = (PIMAGE_NT_HEADERS32)(LoadedImage.FileHeader);
  252. // Exclude the checksum.
  253. ExList.Add(((DWORD_PTR) &NtHeader32->OptionalHeader.CheckSum),
  254. sizeof(NtHeader32->OptionalHeader.CheckSum));
  255. // Exclude the Security directory.
  256. ExList.Add(((DWORD_PTR) &NtHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]),
  257. sizeof(NtHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]));
  258. // Exclude the certs.
  259. ExList.Add((DWORD_PTR)NtHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress +
  260. (DWORD_PTR)LoadedImage.MappedAddress,
  261. NtHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size);
  262. } else {
  263. PIMAGE_NT_HEADERS64 NtHeader64 = (PIMAGE_NT_HEADERS64)(LoadedImage.FileHeader);
  264. // Exclude the checksum.
  265. ExList.Add(((DWORD_PTR) &NtHeader64->OptionalHeader.CheckSum),
  266. sizeof(NtHeader64->OptionalHeader.CheckSum));
  267. // Exclude the Security directory.
  268. ExList.Add(((DWORD_PTR) &NtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]),
  269. sizeof(NtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]));
  270. // Exclude the certs.
  271. ExList.Add((DWORD_PTR)NtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress +
  272. (DWORD_PTR)LoadedImage.MappedAddress,
  273. NtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size);
  274. }
  275. ExList.Emit((PBYTE) (LoadedImage.MappedAddress), LoadedImage.SizeOfImage);
  276. ErrorCode = ERROR_SUCCESS;
  277. } __except(EXCEPTION_EXECUTE_HANDLER) { }
  278. SetLastError(ErrorCode);
  279. return(ErrorCode == ERROR_SUCCESS ? TRUE : FALSE);
  280. }
  281. #pragma warning (pop)
  282. //+=========================================================================
  283. // The following was taken from the following file:
  284. // \nt\sdktools\debuggers\imagehlp\dice.cxx
  285. //-=========================================================================
  286. BOOL
  287. I_FindCertificate(
  288. IN PLOADED_IMAGE LoadedImage,
  289. IN DWORD Index,
  290. OUT LPWIN_CERTIFICATE * Certificate
  291. )
  292. {
  293. PIMAGE_DATA_DIRECTORY pDataDir;
  294. DWORD_PTR CurrentCert = NULL;
  295. BOOL rc;
  296. if (LoadedImage->fDOSImage) {
  297. // No way this could have a certificate;
  298. return(FALSE);
  299. }
  300. rc = FALSE;
  301. __try {
  302. if (LoadedImage->FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
  303. pDataDir = &((PIMAGE_NT_HEADERS32)(LoadedImage->FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
  304. } else if (LoadedImage->FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
  305. pDataDir = &((PIMAGE_NT_HEADERS64)(LoadedImage->FileHeader))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
  306. } else {
  307. __leave; // Not an interesting file type.
  308. }
  309. // Check if the cert pointer is at least reasonable.
  310. if (!pDataDir->VirtualAddress ||
  311. !pDataDir->Size ||
  312. (pDataDir->VirtualAddress + pDataDir->Size > LoadedImage->SizeOfImage))
  313. {
  314. __leave;
  315. }
  316. // We're not looking at an empty security slot or an invalid (past the image boundary) value.
  317. // Let's see if we can find it.
  318. DWORD CurrentIdx = 0;
  319. DWORD_PTR LastCert;
  320. CurrentCert = (DWORD_PTR)(LoadedImage->MappedAddress) + pDataDir->VirtualAddress;
  321. LastCert = CurrentCert + pDataDir->Size;
  322. while (CurrentCert < LastCert ) {
  323. if (CurrentIdx == Index) {
  324. rc = TRUE;
  325. __leave;
  326. }
  327. CurrentIdx++;
  328. CurrentCert += ((LPWIN_CERTIFICATE)CurrentCert)->dwLength;
  329. CurrentCert = (CurrentCert + 7) & ~7; // align it.
  330. }
  331. } __except(EXCEPTION_EXECUTE_HANDLER) { }
  332. if (rc == TRUE) {
  333. *Certificate = (LPWIN_CERTIFICATE)CurrentCert;
  334. }
  335. return(rc);
  336. }
  337. BOOL
  338. WINAPI
  339. imagehack_ImageGetCertificateData(
  340. IN PCRYPT_DATA_BLOB pFileBlob,
  341. IN DWORD CertificateIndex,
  342. OUT LPWIN_CERTIFICATE * Certificate
  343. )
  344. /*++
  345. Routine Description:
  346. Given a specific certificate index, retrieve the certificate data.
  347. Arguments:
  348. FileHandle - Handle to the file in question. The file should be opened
  349. with at least GENERIC_READ access.
  350. CertificateIndex - Index to retrieve
  351. Certificate - Output buffer where the certificate is to be stored.
  352. RequiredLength - Size of the certificate buffer (input). On return, is
  353. set to the actual certificate length. NULL can be used
  354. to determine the size of a certificate.
  355. Return Value:
  356. TRUE - Successful
  357. FALSE - There was some error. Call GetLastError() for more information.
  358. --*/
  359. {
  360. LOADED_IMAGE LoadedImage;
  361. DWORD ErrorCode;
  362. LPWIN_CERTIFICATE ImageCert;
  363. *Certificate = NULL;
  364. // if (I_MapIt(FileHandle, &LoadedImage, MAP_READONLY) == FALSE) {
  365. if (I_MapIt(pFileBlob, &LoadedImage) == FALSE) {
  366. // Unable to map the image.
  367. SetLastError(ERROR_INVALID_PARAMETER);
  368. return(FALSE);
  369. }
  370. ErrorCode = ERROR_INVALID_PARAMETER;
  371. __try {
  372. if (I_FindCertificate(&LoadedImage, CertificateIndex, &ImageCert) == FALSE) {
  373. __leave;
  374. }
  375. *Certificate = ImageCert;
  376. ErrorCode = ERROR_SUCCESS;
  377. } __except(EXCEPTION_EXECUTE_HANDLER) { }
  378. // I_UnMapIt(&LoadedImage);
  379. SetLastError(ErrorCode);
  380. return(ErrorCode == ERROR_SUCCESS ? TRUE: FALSE);
  381. }