├── Build ├── PESignAnalyzer_VS2015_x64.exe └── PESignAnalyzer_VS2015_x86.exe ├── clean.bat ├── LICENSE ├── .gitattributes ├── .gitignore ├── MSVC ├── PESignAnalyzer_VS2013.vcxproj └── PESignAnalyzer_VS2015.vcxproj ├── README.md └── PESignAnalyzer.cpp /Build/PESignAnalyzer_VS2015_x64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leeqwind/PESignAnalyzer/HEAD/Build/PESignAnalyzer_VS2015_x64.exe -------------------------------------------------------------------------------- /Build/PESignAnalyzer_VS2015_x86.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leeqwind/PESignAnalyzer/HEAD/Build/PESignAnalyzer_VS2015_x86.exe -------------------------------------------------------------------------------- /clean.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd 3 | for /r . %%c in (.) do @if exist "%%c\Debug" rd /S /Q "%%c\Debug" 4 | for /r . %%c in (.) do @if exist "%%c\Release" rd /S /Q "%%c\Release" 5 | for /r . %%c in (*.aps *.bsc *.clw *.ilk *.log *.mac *.ncb *.obj *.opt *.sdf *.plg *.positions *.suo *.user *.WW *.i) do del /f /q /s /A "%%c" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Leeqwind 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | 98 | # NuGet Packages Directory 99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 100 | #packages/ 101 | 102 | # Windows Azure Build Output 103 | csx 104 | *.build.csdef 105 | 106 | # Windows Store app package directory 107 | AppPackages/ 108 | 109 | # Others 110 | sql/ 111 | *.Cache 112 | ClientBin/ 113 | [Ss]tyle[Cc]op.* 114 | ~$* 115 | *~ 116 | *.dbmdl 117 | *.[Pp]ublish.xml 118 | *.pfx 119 | *.publishsettings 120 | 121 | # RIA/Silverlight projects 122 | Generated_Code/ 123 | 124 | # Backup & report files from converting an old project file to a newer 125 | # Visual Studio version. Backup files are not needed, because we have git ;-) 126 | _UpgradeReport_Files/ 127 | Backup*/ 128 | UpgradeLog*.XML 129 | UpgradeLog*.htm 130 | 131 | # SQL Server files 132 | App_Data/*.mdf 133 | App_Data/*.ldf 134 | 135 | 136 | #LightSwitch generated files 137 | GeneratedArtifacts/ 138 | _Pvt_Extensions/ 139 | ModelManifest.xml 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac desktop service store files 156 | .DS_Store 157 | -------------------------------------------------------------------------------- /MSVC/PESignAnalyzer_VS2013.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {765EAA83-2A44-4E2C-B8D3-C87A74500614} 15 | Win32Proj 16 | PESignAnalyzer 17 | 18 | 19 | 20 | Application 21 | true 22 | v120 23 | Unicode 24 | 25 | 26 | Application 27 | false 28 | v120 29 | true 30 | Unicode 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | true 44 | $(SolutionDir)$(Configuration)\ 45 | 46 | 47 | false 48 | 49 | 50 | 51 | 52 | 53 | Level3 54 | Disabled 55 | WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 56 | true 57 | 58 | 59 | Console 60 | true 61 | 62 | 63 | 64 | 65 | Level3 66 | 67 | 68 | MaxSpeed 69 | true 70 | true 71 | WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 72 | true 73 | 74 | 75 | Console 76 | true 77 | true 78 | true 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /MSVC/PESignAnalyzer_VS2015.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | {765EAA83-2A44-4E2C-B8D3-C87A74500614} 26 | Win32Proj 27 | PESignAnalyzer 28 | 29 | 30 | 31 | Application 32 | true 33 | v140 34 | Unicode 35 | 36 | 37 | Application 38 | true 39 | v140 40 | Unicode 41 | 42 | 43 | Application 44 | false 45 | v140 46 | true 47 | Unicode 48 | 49 | 50 | Application 51 | false 52 | v140 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | $(SolutionDir)$(Configuration)\ 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | Disabled 91 | WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 92 | true 93 | MultiThreadedDebug 94 | 95 | 96 | Console 97 | true 98 | 99 | 100 | 101 | 102 | 103 | 104 | Level3 105 | Disabled 106 | WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 107 | true 108 | MultiThreadedDebug 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | 119 | 120 | MaxSpeed 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 124 | true 125 | MultiThreaded 126 | 127 | 128 | Console 129 | true 130 | true 131 | true 132 | 133 | 134 | 135 | 136 | Level3 137 | 138 | 139 | MaxSpeed 140 | true 141 | true 142 | WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) 143 | true 144 | MultiThreaded 145 | 146 | 147 | Console 148 | true 149 | true 150 | true 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PESignAnalyzer 2 | 3 | A Simple PE File Signature information Extracting Tool. 4 | 5 | This program is used to get signature information from PE files which signed by a/some embedded code signature certificate(s) on Windows. Supporting multi-signed file info and certificates chain. Runned on Windows Vista, Windows 7, or later OS platform. 6 | 7 | This code uses `CryptoAPIs` to parse the signature and certificate data from specified file, supporting many file types, such as .exe, .cat(catalog file), .dll, .sys, etc. 8 | 9 | 一个简单的PE文件签名信息提取工具。 10 | 11 | 这个程序用来从由1个或多个嵌入式代码签名证书所签名的PE文件中获取签名信息。支持多签名文件信息和证书链的提取。运行在Windows Vista,Windows 7,及更新的操作系统平台。 12 | 13 | 这份代码使用`CryptoAPIs`来解析指定文件中的签名和证书数据,支持多种文件类型,包括exe,cat(catalog文件),dll,sys等格式。 14 | 15 | ## Running Demo 16 | 17 | 运行演示 18 | 19 | ``` 20 | D:\GitHub\PESignAnalyzer\Debug>PESignAnalyzer_vs2013.exe C:\Windows\notepad.exe 21 | filepath: C:\Windows\notepad.exe 22 | signtype: cataloged 23 | catafile: C:\WINDOWS\system32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\Microsoft-Windows-Client-Features-Package-AutoMerged-shell~31bf3856ad364e35~amd64~~10.0.14393.0.cat 24 | ----------------------- 25 | [ The 1 Sign Info ] 26 | timestamp: 2016/07/16 17:45:27 27 | version: V2 28 | digestAlgorithm: SHA256 29 | |--------------------- 30 | |- subject: Microsoft Windows 31 | |- issuer: Microsoft Windows Production PCA 2011 32 | |- serial: 33000000bce120fdd27cc8ee930000000000bc 33 | |- thumbprint: e85459b23c232db3cb94c7a56d47678f58e8e51e 34 | |- signAlgorithm: sha256RSA(RSA) 35 | |- version: V3 36 | |- notbefore: 2015/08/18 17:15:28 37 | |- notafter: 2016/11/18 17:15:28 38 | |- CRLpoint: http://www.microsoft.com/pkiops/crl/MicWinProPCA2011_2011-10-19.crl 39 | |--------------------- 40 | |- subject: Microsoft Windows Production PCA 2011 41 | |- issuer: Microsoft Root Certificate Authority 2010 42 | |- serial: 61077656000000000008 43 | |- thumbprint: 580a6f4cc4e4b669b9ebdc1b2b3e087b80d0678d 44 | |- signAlgorithm: sha256RSA(RSA) 45 | |- version: V3 46 | |- notbefore: 2011/10/19 18:41:42 47 | |- notafter: 2026/10/19 18:51:42 48 | |- CRLpoint: http://crl.microsoft.com/pki/crl/products/MicRooCerAut_2010-06-23.crl 49 | |--------------------- 50 | |- subject: Microsoft Root Certificate Authority 2010 51 | |- issuer: Microsoft Root Certificate Authority 2010 52 | |- serial: 28cc3a25bfba44ac449a9b586b4339aa 53 | |- thumbprint: 3b1efd3a66ea28b16697394703a72ca340a05bd5 54 | |- signAlgorithm: sha256RSA(RSA) 55 | |- version: V3 56 | |- notbefore: 2010/06/23 21:57:24 57 | |- notafter: 2035/06/23 22:04:01 58 | |- CRLpoint: 59 | ----------------------- 60 | 61 | ``` 62 | 63 | ## Compiling 64 | 65 | 编译 66 | 67 | Developer can compile this program with Microsoft Visual Studio 2008 or later version Visual Studio. The target binary file will be built at Debug or Release folder, depending on which compiling method developers select. 68 | 69 | 开发者可以通过Microsoft Visual Studio 2008或更新版本的Visual Studio来编译这个程序。目标二进制文件会在Debug或Release目录生成,这取决于开发者选择何种编译方式。 70 | 71 | ## Multi-signed Supporting 72 | 73 | 多签名支持 74 | 75 | This code does not use `WinVerifyTrust` to verify and retrieve signature and certificate information, but `CryptoAPIs` instead. 76 | 77 | It might also be noted that this program supports analyzing multi-signed PE files, even though on the OS platforms which does not support multi-signature detecting, such as Windows 7, Windows Vista, etc. Multi-signed PE file means that this file has been signed by more than one embedded code signature certificate. 78 | 79 | If you transfer the path to a multi-signatured file into PESignAnalyzer process, it will show the target information as below. Every `[The X Sign Info]` means a chunk of completed information of a signature block. 80 | 81 | 这份代码没有使用`WinVerifyTrust`来验证和获取签名证书信息,而是用`CryptoAPIs`代替。 82 | 83 | 需要注意的是,这个程序支持解析多签名的PE文件,即使是在诸如Windows 7,Windows Vista这种不支持多签名检测的操作系统平台上。多签名的PE文件意味着这个文件已经被多个嵌入式代码签名证书所签名了。 84 | 85 | 如果你将一个多签名文件的路径作为参数传递给PESignAnalyzer的二进制文件,它会展示如下所示的信息。 每一个`[The X Sign Info]`意味着一个签名的完整信息。 86 | 87 | ``` 88 | D:\GitHub\PESignAnalyzer\Debug>PESignAnalyzer_vs2013.exe D:\sign_samples\multi_sign\sample.sys 89 | filepath: D:\sign_samples\multi_sign\sample.sys 90 | signtype: embedded 91 | catafile: 92 | ----------------------- 93 | [ The 1 Sign Info ] 94 | timestamp: 2015/07/10 22:19:44 95 | version: V2 96 | digestAlgorithm: SHA1 97 | |--------------------- 98 | |- subject: Future Technology Devices International Ltd 99 | |- issuer: VeriSign Class 3 Code Signing 2010 CA 100 | |- serial: 03c3ce928ee0415b782a96d3fb5dc283 101 | |- thumbprint: 055ef6258c59fe21f14d9fa938da92f345e7eb9d 102 | |- signAlgorithm: sha1RSA(RSA) 103 | |- version: V3 104 | |- notbefore: 2013/09/18 00:00:00 105 | |- notafter: 2016/11/16 23:59:59 106 | |- CRLpoint: http://csc3-2010-crl.verisign.com/CSC3-2010.crl 107 | |--------------------- 108 | |- subject: VeriSign Class 3 Code Signing 2010 CA 109 | |- issuer: VeriSign Class 3 Public Primary Certification Authority - G5 110 | |- serial: 5200e5aa2556fc1a86ed96c9d44b33c7 111 | |- thumbprint: 495847a93187cfb8c71f840cb7b41497ad95c64f 112 | |- signAlgorithm: sha1RSA(RSA) 113 | |- version: V3 114 | |- notbefore: 2010/02/08 00:00:00 115 | |- notafter: 2020/02/07 23:59:59 116 | |- CRLpoint: http://crl.verisign.com/pca3-g5.crl 117 | |--------------------- 118 | |- subject: VeriSign Class 3 Public Primary Certification Authority - G5 119 | |- issuer: VeriSign Class 3 Public Primary Certification Authority - G5 120 | |- serial: 18dad19e267de8bb4a2158cdcc6b3b4a 121 | |- thumbprint: 4eb6d578499b1ccf5f581ead56be3d9b6744a5e5 122 | |- signAlgorithm: sha1RSA(RSA) 123 | |- version: V3 124 | |- notbefore: 2006/11/08 00:00:00 125 | |- notafter: 2036/07/16 23:59:59 126 | |- CRLpoint: 127 | ----------------------- 128 | [ The 2 Sign Info ] 129 | timestamp: 2015/07/14 20:13:00 130 | version: V2 131 | digestAlgorithm: SHA256 132 | |--------------------- 133 | |- subject: Microsoft Windows Hardware Compatibility Publisher 134 | |- issuer: Microsoft Windows Third Party Component CA 2012 135 | |- serial: 330000001dc31a761624754f8000000000001d 136 | |- thumbprint: 96c51247e27dae45a1bcd582a0503256f9eaedac 137 | |- signAlgorithm: sha256RSA(RSA) 138 | |- version: V3 139 | |- notbefore: 2014/12/19 19:27:34 140 | |- notafter: 2016/03/19 19:27:34 141 | |- CRLpoint: http://www.microsoft.com/pkiops/crl/Microsoft%20Windows%20Third%20Party%20Component%20CA%202012.crl 142 | |--------------------- 143 | |- subject: Microsoft Windows Third Party Component CA 2012 144 | |- issuer: Microsoft Root Certificate Authority 2010 145 | |- serial: 610baac1000000000009 146 | |- thumbprint: 77a10ebf07542725218cd83a01b521c57bc67f73 147 | |- signAlgorithm: sha256RSA(RSA) 148 | |- version: V3 149 | |- notbefore: 2012/04/18 23:48:38 150 | |- notafter: 2027/04/18 23:58:38 151 | |- CRLpoint: http://crl.microsoft.com/pki/crl/products/MicRooCerAut_2010-06-23.crl 152 | |--------------------- 153 | |- subject: Microsoft Root Certificate Authority 2010 154 | |- issuer: Microsoft Root Certificate Authority 2010 155 | |- serial: 28cc3a25bfba44ac449a9b586b4339aa 156 | |- thumbprint: 3b1efd3a66ea28b16697394703a72ca340a05bd5 157 | |- signAlgorithm: sha256RSA(RSA) 158 | |- version: V3 159 | |- notbefore: 2010/06/23 21:57:24 160 | |- notafter: 2035/06/23 22:04:01 161 | |- CRLpoint: 162 | ----------------------- 163 | [ The 3 Sign Info ] 164 | timestamp: 2015/07/24 06:16:44 165 | version: V2 166 | digestAlgorithm: SHA256 167 | |--------------------- 168 | |- subject: Microsoft Windows Hardware Compatibility Publisher 169 | |- issuer: Microsoft Windows Third Party Component CA 2012 170 | |- serial: 330000001dc31a761624754f8000000000001d 171 | |- thumbprint: 96c51247e27dae45a1bcd582a0503256f9eaedac 172 | |- signAlgorithm: sha256RSA(RSA) 173 | |- version: V3 174 | |- notbefore: 2014/12/19 19:27:34 175 | |- notafter: 2016/03/19 19:27:34 176 | |- CRLpoint: http://www.microsoft.com/pkiops/crl/Microsoft%20Windows%20Third%20Party%20Component%20CA%202012.crl 177 | |--------------------- 178 | |- subject: Microsoft Windows Third Party Component CA 2012 179 | |- issuer: Microsoft Root Certificate Authority 2010 180 | |- serial: 610baac1000000000009 181 | |- thumbprint: 77a10ebf07542725218cd83a01b521c57bc67f73 182 | |- signAlgorithm: sha256RSA(RSA) 183 | |- version: V3 184 | |- notbefore: 2012/04/18 23:48:38 185 | |- notafter: 2027/04/18 23:58:38 186 | |- CRLpoint: http://crl.microsoft.com/pki/crl/products/MicRooCerAut_2010-06-23.crl 187 | |--------------------- 188 | |- subject: Microsoft Root Certificate Authority 2010 189 | |- issuer: Microsoft Root Certificate Authority 2010 190 | |- serial: 28cc3a25bfba44ac449a9b586b4339aa 191 | |- thumbprint: 3b1efd3a66ea28b16697394703a72ca340a05bd5 192 | |- signAlgorithm: sha256RSA(RSA) 193 | |- version: V3 194 | |- notbefore: 2010/06/23 21:57:24 195 | |- notafter: 2035/06/23 22:04:01 196 | |- CRLpoint: 197 | ----------------------- 198 | [ The 4 Sign Info ] 199 | timestamp: 2015/09/21 22:47:46 200 | version: V2 201 | digestAlgorithm: SHA256 202 | |--------------------- 203 | |- subject: Microsoft Windows Hardware Compatibility Publisher 204 | |- issuer: Microsoft Windows Third Party Component CA 2012 205 | |- serial: 330000001dc31a761624754f8000000000001d 206 | |- thumbprint: 96c51247e27dae45a1bcd582a0503256f9eaedac 207 | |- signAlgorithm: sha256RSA(RSA) 208 | |- version: V3 209 | |- notbefore: 2014/12/19 19:27:34 210 | |- notafter: 2016/03/19 19:27:34 211 | |- CRLpoint: http://www.microsoft.com/pkiops/crl/Microsoft%20Windows%20Third%20Party%20Component%20CA%202012.crl 212 | |--------------------- 213 | |- subject: Microsoft Windows Third Party Component CA 2012 214 | |- issuer: Microsoft Root Certificate Authority 2010 215 | |- serial: 610baac1000000000009 216 | |- thumbprint: 77a10ebf07542725218cd83a01b521c57bc67f73 217 | |- signAlgorithm: sha256RSA(RSA) 218 | |- version: V3 219 | |- notbefore: 2012/04/18 23:48:38 220 | |- notafter: 2027/04/18 23:58:38 221 | |- CRLpoint: http://crl.microsoft.com/pki/crl/products/MicRooCerAut_2010-06-23.crl 222 | |--------------------- 223 | |- subject: Microsoft Root Certificate Authority 2010 224 | |- issuer: Microsoft Root Certificate Authority 2010 225 | |- serial: 28cc3a25bfba44ac449a9b586b4339aa 226 | |- thumbprint: 3b1efd3a66ea28b16697394703a72ca340a05bd5 227 | |- signAlgorithm: sha256RSA(RSA) 228 | |- version: V3 229 | |- notbefore: 2010/06/23 21:57:24 230 | |- notafter: 2035/06/23 22:04:01 231 | |- CRLpoint: 232 | ----------------------- 233 | 234 | ``` 235 | 236 | ## Contact 237 | 238 | If you have any questions or problems, you can contact with me: leeq.live@outlook.com 239 | -------------------------------------------------------------------------------- /PESignAnalyzer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * COPYRIGHT NOTICE & DESCRIPTION 3 | * 4 | * Source: PESignAnalyzer.cpp 5 | * Author: leeqwind 6 | * E-mail: leeqw.live@outlook.com 7 | * Notice: This program can retrieve signature information from PE 8 | * files which signed by a/some certificate(s) on Windows. 9 | * Supporting multi-signed information and certificates chain. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | using namespace std; 27 | 28 | #define MY_ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) 29 | #ifndef szOID_RFC3161_counterSign 30 | #define szOID_RFC3161_counterSign "1.3.6.1.4.1.311.3.3.1" 31 | #endif 32 | #ifndef szOID_NESTED_SIGNATURE 33 | #define szOID_NESTED_SIGNATURE "1.3.6.1.4.1.311.2.4.1" 34 | #endif 35 | 36 | #pragma comment(lib, "Crypt32.lib") 37 | #pragma comment(lib, "Wintrust.lib") 38 | 39 | typedef struct _SIGN_COUNTER_SIGN { 40 | std::string SignerName; 41 | std::string MailAddress; 42 | std::string TimeStamp; 43 | } SIGN_COUNTER_SIGN, *PSIGN_COUNTER_SIGN; 44 | 45 | /// Per certificate node. 46 | typedef struct _CERT_NODE_INFO { 47 | std::string SubjectName; 48 | std::string IssuerName; 49 | std::string Version; 50 | std::string Serial; 51 | std::string Thumbprint; 52 | std::string NotBefore; 53 | std::string NotAfter; 54 | std::string SignAlgorithm; 55 | std::wstring CRLpoint; 56 | } CERT_NODE_INFO, *PCERT_NODE_INFO; 57 | 58 | /// Per signature node. 59 | typedef struct _SIGN_NODE_INFO { 60 | std::string DigestAlgorithm; 61 | std::string Version; 62 | SIGN_COUNTER_SIGN CounterSign; 63 | std::list CertChain; 64 | } SIGN_NODE_INFO, *PSIGN_NODE_INFO; 65 | 66 | typedef struct _SIGNDATA_HANDLE { 67 | DWORD dwObjSize; 68 | PCMSG_SIGNER_INFO pSignerInfo; 69 | HCERTSTORE hCertStoreHandle; 70 | } SIGNDATA_HANDLE, *PSIGNDATA_HANDLE; 71 | 72 | BOOL MyCryptMsgGetParam( 73 | HCRYPTMSG hCryptMsg, 74 | DWORD dwParamType, 75 | DWORD dwIndex, 76 | PVOID *pParam, 77 | DWORD *dwOutSize 78 | ) { 79 | BOOL bReturn = FALSE; 80 | DWORD dwSize = 0; 81 | if (!pParam) 82 | { 83 | return FALSE; 84 | } 85 | // Get size 86 | bReturn = CryptMsgGetParam(hCryptMsg, dwParamType, dwIndex, NULL, &dwSize); 87 | if (!bReturn) 88 | { 89 | return FALSE; 90 | } 91 | // Alloc memory via size 92 | *pParam = (PVOID)LocalAlloc(LPTR, dwSize); 93 | if (!*pParam) 94 | { 95 | return FALSE; 96 | } 97 | // Get data to alloced memory 98 | bReturn = CryptMsgGetParam(hCryptMsg, dwParamType, dwIndex, *pParam, &dwSize); 99 | if (!bReturn) 100 | { 101 | return FALSE; 102 | } 103 | if (dwOutSize) 104 | { 105 | *dwOutSize = dwSize; 106 | } 107 | return TRUE; 108 | } 109 | 110 | CONST UCHAR SG_ProtoCoded[] = { 111 | 0x30, 0x82, 112 | }; 113 | 114 | CONST UCHAR SG_SignedData[] = { 115 | 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 116 | }; 117 | 118 | #define XCH_WORD_LITEND(num) \ 119 | (WORD)(((((WORD)num) & 0xFF00) >> 8) | ((((WORD)num) & 0x00FF) << 8)) 120 | 121 | #define _8BYTE_ALIGN(offset, base) \ 122 | (((offset + base + 7) & 0xFFFFFFF8L) - (base & 0xFFFFFFF8L)) 123 | 124 | // https://msdn.microsoft.com/zh-cn/library/windows/desktop/aa374890(v=vs.85).aspx 125 | BOOL GetNestedSignerInfo( 126 | CONST PSIGNDATA_HANDLE AuthSignData, 127 | std::list & NestedChain 128 | ) { 129 | BOOL bSucceed = FALSE; 130 | BOOL bReturn = FALSE; 131 | HCRYPTMSG hNestedMsg = NULL; 132 | PBYTE pbCurrData = NULL; 133 | PBYTE pbNextData = NULL; 134 | DWORD n = 0x00; 135 | DWORD cbCurrData = 0x00; 136 | 137 | if (!AuthSignData->pSignerInfo) 138 | { 139 | return FALSE; 140 | } 141 | __try 142 | { 143 | // Traverse and look for a nested signature. 144 | for (n = 0; n < AuthSignData->pSignerInfo->UnauthAttrs.cAttr; n++) 145 | { 146 | if (!lstrcmpA(AuthSignData->pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, 147 | szOID_NESTED_SIGNATURE)) 148 | { 149 | break; 150 | } 151 | } 152 | // Cannot find a nested signature attribute. 153 | if (n >= AuthSignData->pSignerInfo->UnauthAttrs.cAttr) 154 | { 155 | bSucceed = FALSE; 156 | __leave; 157 | } 158 | pbCurrData = AuthSignData->pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData; 159 | cbCurrData = AuthSignData->pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData; 160 | // Multiple nested signatures just add one attr in UnauthAttrs 161 | // list of the main signature pointing to the first nested si- 162 | // gnature. Every nested signature exists side by side in an 8 163 | // bytes aligned way. According to the size of major signature 164 | // parse the nested signatures one by one. 165 | while (pbCurrData > (BYTE *)AuthSignData->pSignerInfo && 166 | pbCurrData < (BYTE *)AuthSignData->pSignerInfo + AuthSignData->dwObjSize) 167 | { 168 | SIGNDATA_HANDLE NestedHandle = { 0 }; 169 | // NOTE: The size in 30 82 xx doesnt contain its own size. 170 | // HEAD: 171 | // 0000: 30 82 04 df ; SEQUENCE (4df Bytes) 172 | // 0004: 06 09 ; OBJECT_ID(9 Bytes) 173 | // 0006: | 2a 86 48 86 f7 0d 01 07 02 174 | // | ; 1.2.840.113549.1.7.2 PKCS 7 SignedData 175 | if (memcmp(pbCurrData + 0, SG_ProtoCoded, sizeof(SG_ProtoCoded)) || 176 | memcmp(pbCurrData + 6, SG_SignedData, sizeof(SG_SignedData))) 177 | { 178 | break; 179 | } 180 | 181 | if (hNestedMsg) { 182 | CryptMsgClose(hNestedMsg); 183 | hNestedMsg = NULL; 184 | } 185 | hNestedMsg = CryptMsgOpenToDecode(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 186 | 0, 187 | 0, 188 | 0, 189 | NULL, 190 | 0 191 | ); 192 | if (!hNestedMsg) // Fatal Error 193 | { 194 | bSucceed = FALSE; 195 | __leave; 196 | } 197 | 198 | // Big Endian -> Little Endian 199 | cbCurrData = XCH_WORD_LITEND(*(WORD *)(pbCurrData + 2)) + 4; 200 | pbNextData = pbCurrData; 201 | pbNextData += _8BYTE_ALIGN(cbCurrData, (ULONG_PTR)pbCurrData); 202 | bReturn = CryptMsgUpdate(hNestedMsg, pbCurrData, cbCurrData, TRUE); 203 | pbCurrData = pbNextData; 204 | if (!bReturn) 205 | { 206 | continue; 207 | } 208 | bReturn = MyCryptMsgGetParam(hNestedMsg, CMSG_SIGNER_INFO_PARAM, 209 | 0, 210 | (PVOID *)&NestedHandle.pSignerInfo, 211 | &NestedHandle.dwObjSize 212 | ); 213 | if (!bReturn) 214 | { 215 | continue; 216 | } 217 | NestedHandle.hCertStoreHandle = CertOpenStore(CERT_STORE_PROV_MSG, 218 | PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 219 | 0, 220 | 0, 221 | hNestedMsg 222 | ); 223 | bSucceed = TRUE; 224 | NestedChain.push_back(NestedHandle); 225 | } 226 | } 227 | __finally 228 | { 229 | if (hNestedMsg) CryptMsgClose(hNestedMsg); 230 | } 231 | return bSucceed; 232 | } 233 | 234 | BOOL GetAuthedAttribute( 235 | PCMSG_SIGNER_INFO pSignerInfo 236 | ) { 237 | BOOL bSucceed = FALSE; 238 | DWORD dwObjSize = 0x00; 239 | DWORD n = 0x00; 240 | 241 | __try 242 | { 243 | for (n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++) 244 | { 245 | if (!lstrcmpA(pSignerInfo->AuthAttrs.rgAttr[n].pszObjId, szOID_RSA_counterSign)) 246 | { 247 | bSucceed = TRUE; 248 | break; 249 | } 250 | } 251 | } 252 | __finally 253 | { 254 | } 255 | return bSucceed; 256 | } 257 | 258 | // http://support.microsoft.com/kb/323809 259 | BOOL GetCounterSignerInfo( 260 | PCMSG_SIGNER_INFO pSignerInfo, 261 | PCMSG_SIGNER_INFO *pTargetSigner 262 | ) { 263 | BOOL bSucceed = FALSE; 264 | BOOL bReturn = FALSE; 265 | DWORD dwObjSize = 0x00; 266 | DWORD n = 0x00; 267 | 268 | if (!pSignerInfo || !pTargetSigner) 269 | { 270 | return FALSE; 271 | } 272 | __try 273 | { 274 | *pTargetSigner = NULL; 275 | for (n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++) 276 | { 277 | if (!lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, szOID_RSA_counterSign)) 278 | { 279 | break; 280 | } 281 | } 282 | if (n >= pSignerInfo->UnauthAttrs.cAttr) 283 | { 284 | bSucceed = FALSE; 285 | __leave; 286 | } 287 | bReturn = CryptDecodeObject(MY_ENCODING, 288 | PKCS7_SIGNER_INFO, 289 | pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData, 290 | pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData, 291 | 0, 292 | NULL, 293 | &dwObjSize 294 | ); 295 | if (!bReturn) 296 | { 297 | bSucceed = FALSE; 298 | __leave; 299 | } 300 | *pTargetSigner = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwObjSize); 301 | if (!*pTargetSigner) 302 | { 303 | bSucceed = FALSE; 304 | __leave; 305 | } 306 | bReturn = CryptDecodeObject(MY_ENCODING, 307 | PKCS7_SIGNER_INFO, 308 | pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData, 309 | pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData, 310 | 0, 311 | (PVOID)*pTargetSigner, 312 | &dwObjSize 313 | ); 314 | if (!bReturn) 315 | { 316 | bSucceed = FALSE; 317 | __leave; 318 | } 319 | bSucceed = TRUE; 320 | } 321 | __finally 322 | { 323 | } 324 | return bSucceed; 325 | } 326 | 327 | std::string TimeToString( 328 | FILETIME *pftIn, 329 | SYSTEMTIME *pstIn = NULL 330 | ) { 331 | SYSTEMTIME st = { 0 }; 332 | CHAR szBuffer[256] = { 0 }; 333 | 334 | if (!pstIn) 335 | { 336 | if (!pftIn) 337 | { 338 | return std::string(""); 339 | } 340 | FileTimeToSystemTime(pftIn, &st); 341 | pstIn = &st; 342 | } 343 | _snprintf_s(szBuffer, 256, "%04d/%02d/%02d %02d:%02d:%02d", 344 | pstIn->wYear, 345 | pstIn->wMonth, 346 | pstIn->wDay, 347 | pstIn->wHour, 348 | pstIn->wMinute, 349 | pstIn->wSecond 350 | ); 351 | return std::string(szBuffer); 352 | } 353 | 354 | BOOL GetCounterSignerData( 355 | CONST PCMSG_SIGNER_INFO SignerInfo, 356 | SIGN_COUNTER_SIGN & CounterSign 357 | ) { 358 | BOOL bReturn = FALSE; 359 | DWORD n = 0x00; 360 | DWORD dwData = 0x00; 361 | FILETIME lft, ft; 362 | SYSTEMTIME st; 363 | 364 | // Find szOID_RSA_signingTime OID. 365 | for (n = 0; n < SignerInfo->AuthAttrs.cAttr; n++) 366 | { 367 | if (!lstrcmpA(SignerInfo->AuthAttrs.rgAttr[n].pszObjId, szOID_RSA_signingTime)) 368 | { 369 | break; 370 | } 371 | } 372 | if (n >= SignerInfo->AuthAttrs.cAttr) 373 | { 374 | return FALSE; 375 | } 376 | // Decode and get FILETIME structure. 377 | dwData = sizeof(ft); 378 | bReturn = CryptDecodeObject(MY_ENCODING, 379 | szOID_RSA_signingTime, 380 | SignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData, 381 | SignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData, 382 | 0, 383 | (PVOID)&ft, 384 | &dwData 385 | ); 386 | if (!bReturn) 387 | { 388 | return FALSE; 389 | } 390 | // Convert. 391 | FileTimeToLocalFileTime(&ft, &lft); 392 | FileTimeToSystemTime(&lft, &st); 393 | CounterSign.TimeStamp = TimeToString(NULL, &st); 394 | return TRUE; 395 | } 396 | 397 | BOOL SafeToReadNBytes( 398 | DWORD dwSize, 399 | DWORD dwStart, 400 | DWORD dwRequestSize 401 | ) { 402 | return dwSize - dwStart >= dwRequestSize; 403 | } 404 | 405 | void ParseDERType( 406 | BYTE bIn, 407 | INT & iType, 408 | INT & iClass 409 | ) { 410 | iType = bIn & 0x3F; 411 | iClass = bIn >> 6; 412 | } 413 | 414 | DWORD ReadNumberFromNBytes( 415 | PBYTE pbSignature, 416 | DWORD dwStart, 417 | DWORD dwRequestSize 418 | ) { 419 | DWORD dwNumber = 0; 420 | for (DWORD i = 0; i < dwRequestSize; i++) 421 | { 422 | dwNumber = dwNumber * 0x100 + pbSignature[dwStart + i]; 423 | } 424 | return dwNumber; 425 | } 426 | 427 | BOOL ParseDERSize( 428 | PBYTE pbSignature, 429 | DWORD dwSize, 430 | DWORD & dwSizefound, 431 | DWORD & dwBytesParsed 432 | ) { 433 | if (pbSignature[0] > 0x80 && 434 | !SafeToReadNBytes(dwSize, 1, pbSignature[0] - 0x80)) 435 | { 436 | return FALSE; 437 | } 438 | if (pbSignature[0] <= 0x80) 439 | { 440 | dwSizefound = pbSignature[0]; 441 | dwBytesParsed = 1; 442 | } 443 | else 444 | { 445 | dwSizefound = ReadNumberFromNBytes(pbSignature, 1, pbSignature[0] - 0x80); 446 | dwBytesParsed = 1 + pbSignature[0] - 0x80; 447 | } 448 | return TRUE; 449 | } 450 | 451 | BOOL ParseDERFindType( 452 | INT iTypeSearch, 453 | PBYTE pbSignature, 454 | DWORD dwSize, 455 | DWORD & dwPositionFound, 456 | DWORD & dwLengthFound, 457 | DWORD & dwPositionError, 458 | INT & iTypeError 459 | ) { 460 | DWORD dwPosition = 0; 461 | DWORD dwSizeFound = 0; 462 | DWORD dwBytesParsed = 0; 463 | INT iType = 0; 464 | INT iClass = 0; 465 | 466 | iTypeError = -1; 467 | dwPositionFound = 0; 468 | dwLengthFound = 0; 469 | dwPositionError = 0; 470 | if (NULL == pbSignature) 471 | { 472 | iTypeError = -1; 473 | return FALSE; 474 | } 475 | while (dwSize > dwPosition) 476 | { 477 | if (!SafeToReadNBytes(dwSize, dwPosition, 2)) 478 | { 479 | dwPositionError = dwPosition; 480 | iTypeError = -2; 481 | return FALSE; 482 | } 483 | ParseDERType(pbSignature[dwPosition], iType, iClass); 484 | switch (iType) 485 | { 486 | case 0x05: // NULL 487 | dwPosition++; 488 | if (pbSignature[dwPosition] != 0x00) 489 | { 490 | dwPositionError = dwPosition; 491 | iTypeError = -4; 492 | return FALSE; 493 | } 494 | dwPosition++; 495 | break; 496 | 497 | case 0x06: // OID 498 | dwPosition++; 499 | if (!SafeToReadNBytes(dwSize - dwPosition, 1, pbSignature[dwPosition])) 500 | { 501 | dwPositionError = dwPosition; 502 | iTypeError = -5; 503 | return FALSE; 504 | } 505 | dwPosition += 1 + pbSignature[dwPosition]; 506 | break; 507 | 508 | case 0x00: // ? 509 | case 0x01: // boolean 510 | case 0x02: // integer 511 | case 0x03: // bit std::string 512 | case 0x04: // octec std::string 513 | case 0x0A: // enumerated 514 | case 0x0C: // UTF8string 515 | case 0x13: // printable std::string 516 | case 0x14: // T61 std::string 517 | case 0x16: // IA5String 518 | case 0x17: // UTC time 519 | case 0x18: // Generalized time 520 | case 0x1E: // BMPstring 521 | dwPosition++; 522 | if (!ParseDERSize(pbSignature + dwPosition, dwSize - dwPosition, 523 | dwSizeFound, 524 | dwBytesParsed)) 525 | { 526 | dwPositionError = dwPosition; 527 | iTypeError = -7; 528 | return FALSE; 529 | } 530 | dwPosition += dwBytesParsed; 531 | if (!SafeToReadNBytes(dwSize - dwPosition, 0, dwSizeFound)) 532 | { 533 | dwPositionError = dwPosition; 534 | iTypeError = -8; 535 | return FALSE; 536 | } 537 | if (iTypeSearch == iType) 538 | { 539 | dwPositionFound = dwPosition; 540 | dwLengthFound = dwSizeFound; 541 | return TRUE; 542 | } 543 | dwPosition += dwSizeFound; 544 | break; 545 | 546 | case 0x20: // context specific 547 | case 0x21: // context specific 548 | case 0x23: // context specific 549 | case 0x24: // context specific 550 | case 0x30: // sequence 551 | case 0x31: // set 552 | dwPosition++; 553 | if (!ParseDERSize(pbSignature + dwPosition, dwSize - dwPosition, 554 | dwSizeFound, 555 | dwBytesParsed)) 556 | { 557 | dwPositionError = dwPosition; 558 | iTypeError = -9; 559 | return FALSE; 560 | } 561 | dwPosition += dwBytesParsed; 562 | break; 563 | 564 | case 0x22: // ? 565 | dwPosition += 2; 566 | break; 567 | 568 | default: 569 | dwPositionError = dwPosition; 570 | iTypeError = iType; 571 | return FALSE; 572 | } 573 | } 574 | return FALSE; 575 | } 576 | 577 | BOOL GetGeneralizedTimeStamp( 578 | PCMSG_SIGNER_INFO pSignerInfo, 579 | std::string & TimeStamp 580 | ) { 581 | BOOL bSucceed = FALSE; 582 | BOOL bReturn = FALSE; 583 | DWORD dwPositionFound = 0; 584 | DWORD dwLengthFound = 0; 585 | DWORD dwPositionError = 0; 586 | DWORD n = 0; 587 | INT iTypeError = 0; 588 | SYSTEMTIME sst, lst; 589 | FILETIME fft, lft; 590 | 591 | ULONG wYear = 0; 592 | ULONG wMonth = 0; 593 | ULONG wDay = 0; 594 | ULONG wHour = 0; 595 | ULONG wMinute = 0; 596 | ULONG wSecond = 0; 597 | ULONG wMilliseconds = 0; 598 | 599 | for (n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++) 600 | { 601 | if (!lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, szOID_RFC3161_counterSign)) 602 | { 603 | break; 604 | } 605 | } 606 | if (n >= pSignerInfo->UnauthAttrs.cAttr) 607 | { 608 | return FALSE; 609 | } 610 | bReturn = ParseDERFindType(0x04, 611 | pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData, 612 | pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData, 613 | dwPositionFound, 614 | dwLengthFound, 615 | dwPositionError, 616 | iTypeError 617 | ); 618 | if (!bReturn) 619 | { 620 | return FALSE; 621 | } 622 | PBYTE pbOctetString = &pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData[dwPositionFound]; 623 | bReturn = ParseDERFindType(0x18, pbOctetString, dwLengthFound, 624 | dwPositionFound, 625 | dwLengthFound, 626 | dwPositionError, 627 | iTypeError 628 | ); 629 | if (!bReturn) 630 | { 631 | return FALSE; 632 | } 633 | CHAR szBuffer[256]; 634 | strncpy_s(szBuffer, (CHAR *)&(pbOctetString[dwPositionFound]), dwLengthFound); 635 | szBuffer[dwLengthFound] = 0; 636 | _snscanf_s(szBuffer, 256, "%04d%02d%02d%02d%02d%02d.%03dZ", 637 | &wYear, 638 | &wMonth, 639 | &wDay, 640 | &wHour, 641 | &wMinute, 642 | &wSecond, 643 | &wMilliseconds 644 | ); 645 | sst.wYear = (WORD)wYear; 646 | sst.wMonth = (WORD)wMonth; 647 | sst.wDay = (WORD)wDay; 648 | sst.wHour = (WORD)wHour; 649 | sst.wMinute = (WORD)wMinute; 650 | sst.wSecond = (WORD)wSecond; 651 | sst.wMilliseconds = (WORD)wMilliseconds; 652 | SystemTimeToFileTime(&sst, &fft); 653 | FileTimeToLocalFileTime(&fft, &lft); 654 | FileTimeToSystemTime(&lft, &lst); 655 | TimeStamp = TimeToString(NULL, &lst); 656 | return TRUE; 657 | } 658 | 659 | INT IsCharacterToStrip( 660 | INT Character 661 | ) { 662 | return 0 == Character || '\t' == Character || '\n' == Character || '\r' == Character; 663 | } 664 | 665 | VOID StripString( 666 | std::string & StrArg 667 | ) { 668 | StrArg.erase(remove_if(StrArg.begin(), StrArg.end(), IsCharacterToStrip), StrArg.end()); 669 | } 670 | 671 | BOOL GetStringFromCertContext( 672 | PCCERT_CONTEXT pCertContext, 673 | DWORD Type, 674 | DWORD Flag, 675 | std::string & String 676 | ) { 677 | DWORD dwData = 0x00; 678 | LPSTR pszTempName = NULL; 679 | 680 | dwData = CertGetNameStringA(pCertContext, Type, Flag, NULL, NULL, 0); 681 | if (!dwData) 682 | { 683 | CertFreeCertificateContext(pCertContext); 684 | return FALSE; 685 | } 686 | pszTempName = (LPSTR)LocalAlloc(LPTR, dwData * sizeof(CHAR)); 687 | if (!pszTempName) 688 | { 689 | CertFreeCertificateContext(pCertContext); 690 | return FALSE; 691 | } 692 | dwData = CertGetNameStringA(pCertContext, Type, Flag, NULL, pszTempName, dwData); 693 | if (!dwData) 694 | { 695 | LocalFree(pszTempName); 696 | return FALSE; 697 | } 698 | String = std::string(pszTempName); 699 | StripString(String); 700 | LocalFree(pszTempName); 701 | return TRUE; 702 | } 703 | 704 | BOOL CalculateSignVersion( 705 | DWORD dwVersion, 706 | std::string & Version 707 | ) { 708 | switch (dwVersion) 709 | { 710 | case CERT_V1: 711 | Version = "V1"; 712 | break; 713 | case CERT_V2: 714 | Version = "V2"; 715 | break; 716 | case CERT_V3: 717 | Version = "V3"; 718 | break; 719 | default: 720 | Version = "Unknown"; 721 | break; 722 | } 723 | StripString(Version); 724 | return TRUE; 725 | } 726 | 727 | BOOL CalculateDigestAlgorithm( 728 | LPCSTR pszObjId, 729 | std::string & Algorithm 730 | ) { 731 | if (!pszObjId) 732 | { 733 | Algorithm = "Unknown"; 734 | } 735 | else if (!strcmp(pszObjId, szOID_OIWSEC_sha1)) 736 | { 737 | Algorithm = "SHA1"; 738 | } 739 | else if (!strcmp(pszObjId, szOID_RSA_MD5)) 740 | { 741 | Algorithm = "MD5"; 742 | } 743 | else if (!strcmp(pszObjId, szOID_NIST_sha256)) 744 | { 745 | Algorithm = "SHA256"; 746 | } 747 | else 748 | { 749 | Algorithm = std::string(pszObjId); 750 | } 751 | StripString(Algorithm); 752 | return TRUE; 753 | } 754 | 755 | BOOL CalculateCertAlgorithm( 756 | LPCSTR pszObjId, 757 | std::string & Algorithm 758 | ) { 759 | if (!pszObjId) 760 | { 761 | Algorithm = "Unknown"; 762 | } 763 | else if (0 == strcmp(pszObjId, szOID_RSA_SHA1RSA)) 764 | { 765 | Algorithm = "sha1RSA(RSA)"; 766 | } 767 | else if (0 == strcmp(pszObjId, szOID_OIWSEC_sha1RSASign)) 768 | { 769 | Algorithm = "sha1RSA(OIW)"; 770 | } 771 | else if (0 == strcmp(pszObjId, szOID_RSA_MD5RSA)) 772 | { 773 | Algorithm = "md5RSA(RSA)"; 774 | } 775 | else if (0 == strcmp(pszObjId, szOID_OIWSEC_md5RSA)) 776 | { 777 | Algorithm = "md5RSA(OIW)"; 778 | } 779 | else if (0 == strcmp(pszObjId, szOID_RSA_MD2RSA)) 780 | { 781 | Algorithm = "md2RSA(RSA)"; 782 | } 783 | else if (0 == strcmp(pszObjId, szOID_RSA_SHA256RSA)) 784 | { 785 | Algorithm = "sha256RSA(RSA)"; 786 | } 787 | else 788 | { 789 | Algorithm = pszObjId; 790 | } 791 | StripString(Algorithm); 792 | return TRUE; 793 | } 794 | 795 | #define SHA1LEN 20 796 | #define BUFSIZE 2048 797 | #define MD5LEN 16 798 | 799 | BOOL CalculateHashOfBytes( 800 | BYTE *pbBinary, 801 | ALG_ID Algid, 802 | DWORD dwBinary, 803 | std::string & Hash 804 | ) { 805 | BOOL bReturn = FALSE; 806 | DWORD dwLastError = 0; 807 | HCRYPTPROV hProv = 0; 808 | HCRYPTHASH hHash = 0; 809 | DWORD cbHash = 0; 810 | BYTE rgbHash[SHA1LEN] = { 0 }; 811 | CHAR hexbyte[3] = { 0 }; 812 | CONST CHAR rgbDigits[] = "0123456789abcdef"; 813 | std::string CalcHash; 814 | 815 | bReturn = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); 816 | if (!bReturn) 817 | { 818 | dwLastError = GetLastError(); 819 | return FALSE; 820 | } 821 | bReturn = CryptCreateHash(hProv, Algid, 0, 0, &hHash); 822 | if (!bReturn) 823 | { 824 | dwLastError = GetLastError(); 825 | CryptReleaseContext(hProv, 0); 826 | return FALSE; 827 | } 828 | bReturn = CryptHashData(hHash, pbBinary, dwBinary, 0); 829 | if (!bReturn) 830 | { 831 | dwLastError = GetLastError(); 832 | CryptDestroyHash(hHash); 833 | CryptReleaseContext(hProv, 0); 834 | return FALSE; 835 | } 836 | if (CALG_SHA1 == Algid) 837 | { 838 | cbHash = SHA1LEN; 839 | } 840 | else if (CALG_MD5 == Algid) 841 | { 842 | cbHash = MD5LEN; 843 | } 844 | else 845 | { 846 | cbHash = 0; 847 | } 848 | hexbyte[2] = '\0'; 849 | bReturn = CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0); 850 | if (!bReturn) 851 | { 852 | dwLastError = GetLastError(); 853 | CryptDestroyHash(hHash); 854 | CryptReleaseContext(hProv, 0); 855 | return FALSE; 856 | } 857 | for (DWORD i = 0; i < cbHash; i++) 858 | { 859 | hexbyte[0] = rgbDigits[rgbHash[i] >> 4]; 860 | hexbyte[1] = rgbDigits[rgbHash[i] & 0xf]; 861 | CalcHash.append(hexbyte); 862 | } 863 | Hash = CalcHash; 864 | CryptDestroyHash(hHash); 865 | CryptReleaseContext(hProv, 0); 866 | return TRUE; 867 | } 868 | 869 | BOOL CalculateCertCRLpoint( 870 | DWORD cExtensions, 871 | CERT_EXTENSION rgExtensions[], 872 | std::wstring & CRLpoint 873 | ) { 874 | BOOL bReturn = FALSE; 875 | BYTE btData[512] = { 0 }; 876 | WCHAR csProperty[512] = { 0 }; 877 | ULONG ulDataLen = 512; 878 | PCRL_DIST_POINTS_INFO pCRLDistPoint = (PCRL_DIST_POINTS_INFO)btData; 879 | PCRL_DIST_POINT_NAME dpn = NULL; 880 | PCERT_EXTENSION pe = NULL; 881 | 882 | CRLpoint.clear(); 883 | pe = CertFindExtension(szOID_CRL_DIST_POINTS, cExtensions, rgExtensions); 884 | if (!pe) 885 | { 886 | return FALSE; 887 | } 888 | bReturn = CryptDecodeObject(MY_ENCODING, szOID_CRL_DIST_POINTS, 889 | pe->Value.pbData, 890 | pe->Value.cbData, 891 | CRYPT_DECODE_NOCOPY_FLAG, 892 | pCRLDistPoint, &ulDataLen 893 | ); 894 | if (!bReturn) 895 | { 896 | return FALSE; 897 | } 898 | for (ULONG idx = 0; idx < pCRLDistPoint->cDistPoint; idx++) 899 | { 900 | dpn = &pCRLDistPoint->rgDistPoint[idx].DistPointName; 901 | for (ULONG ulAltEntry = 0; ulAltEntry < dpn->FullName.cAltEntry; ulAltEntry++) 902 | { 903 | if (wcslen(csProperty) > 0) 904 | { 905 | wcscat_s(csProperty, 512, L";"); 906 | } 907 | wcscat_s(csProperty, 512, dpn->FullName.rgAltEntry[ulAltEntry].pwszURL); 908 | } 909 | } 910 | CRLpoint = csProperty; 911 | return TRUE; 912 | } 913 | 914 | BOOL CalculateSignSerial( 915 | BYTE *pbData, 916 | DWORD cbData, 917 | std::string & Serial 918 | ) { 919 | BOOL bReturn = FALSE; 920 | DWORD dwSize = 0x400; 921 | BYTE abSerial[0x400] = { 0 }; 922 | CHAR NameBuff[0x400] = { 0 }; 923 | 924 | Serial.clear(); 925 | for (UINT uiIter = 0; uiIter < cbData && uiIter < 0x400; uiIter++) 926 | { 927 | abSerial[uiIter] = pbData[cbData - 1 - uiIter]; 928 | } 929 | bReturn = CryptBinaryToStringA(abSerial, cbData, CRYPT_STRING_HEX, NameBuff, &dwSize); 930 | if (!bReturn) 931 | { 932 | return FALSE; 933 | } 934 | DWORD dwIter1 = 0; 935 | DWORD dwIter2 = 0; 936 | for (dwIter1 = 0; dwIter1 < dwSize; dwIter1++) 937 | { 938 | if (!isspace(NameBuff[dwIter1])) 939 | { 940 | NameBuff[dwIter2++] = NameBuff[dwIter1]; 941 | } 942 | } 943 | NameBuff[dwIter2] = '\0'; 944 | Serial = std::string(NameBuff); 945 | StripString(Serial); 946 | return TRUE; 947 | } 948 | 949 | // Getting Signer Signature Information. 950 | // If return TRUE, it will continue for caller; 951 | // else, jump out the while loop. 952 | BOOL GetSignerSignatureInfo( 953 | CONST HCERTSTORE hSystemStore, 954 | CONST HCERTSTORE hCertStore, 955 | CONST PCCERT_CONTEXT pOrigContext, 956 | PCCERT_CONTEXT & pCurrContext, 957 | SIGN_NODE_INFO & SignNode 958 | ) { 959 | BOOL bReturn = FALSE; 960 | PCERT_INFO pCertInfo = pCurrContext->pCertInfo; 961 | LPCSTR szObjId = NULL; 962 | CERT_NODE_INFO CertNode; 963 | 964 | // Get certificate algorithm. 965 | szObjId = pCertInfo->SignatureAlgorithm.pszObjId; 966 | bReturn = CalculateCertAlgorithm(szObjId, CertNode.SignAlgorithm); 967 | // Get certificate serial. 968 | bReturn = CalculateSignSerial(pCertInfo->SerialNumber.pbData, 969 | pCertInfo->SerialNumber.cbData, 970 | CertNode.Serial 971 | ); 972 | // Get certificate version. 973 | bReturn = CalculateSignVersion(pCertInfo->dwVersion, CertNode.Version); 974 | // Get certficate subject. 975 | bReturn = GetStringFromCertContext(pCurrContext, 976 | CERT_NAME_SIMPLE_DISPLAY_TYPE, 977 | 0, 978 | CertNode.SubjectName 979 | ); 980 | // Get certificate issuer. 981 | bReturn = GetStringFromCertContext(pCurrContext, 982 | CERT_NAME_SIMPLE_DISPLAY_TYPE, 983 | CERT_NAME_ISSUER_FLAG, 984 | CertNode.IssuerName 985 | ); 986 | // Get certificate thumbprint. 987 | bReturn = CalculateHashOfBytes(pCurrContext->pbCertEncoded, 988 | CALG_SHA1, 989 | pCurrContext->cbCertEncoded, 990 | CertNode.Thumbprint 991 | ); 992 | // Get certificate CRL point. 993 | bReturn = CalculateCertCRLpoint(pCertInfo->cExtension, 994 | pCertInfo->rgExtension, 995 | CertNode.CRLpoint 996 | ); 997 | // Get certificate validity. 998 | CertNode.NotBefore = TimeToString(&pCertInfo->NotBefore); 999 | CertNode.NotAfter = TimeToString(&pCertInfo->NotAfter); 1000 | 1001 | SignNode.CertChain.push_back(CertNode); 1002 | 1003 | // Get next certificate link node. 1004 | pCurrContext = CertFindCertificateInStore(hCertStore, 1005 | MY_ENCODING, 1006 | 0, 1007 | CERT_FIND_SUBJECT_NAME, 1008 | (PVOID)&pCertInfo->Issuer, 1009 | NULL 1010 | ); 1011 | // Root certificate is always included pe file certstore, 1012 | // We can find it in system certstore. 1013 | if (!pCurrContext) 1014 | { 1015 | pCurrContext = CertFindCertificateInStore(hSystemStore, 1016 | MY_ENCODING, 1017 | 0, 1018 | CERT_FIND_SUBJECT_NAME, 1019 | (PVOID)&pCertInfo->Issuer, 1020 | NULL 1021 | ); 1022 | } 1023 | if (!pCurrContext) 1024 | { 1025 | return FALSE; 1026 | } 1027 | // Sometimes issuer is equal to subject. Jump out if so. 1028 | return CertComparePublicKeyInfo(MY_ENCODING, 1029 | &pCurrContext->pCertInfo->SubjectPublicKeyInfo, 1030 | &pOrigContext->pCertInfo->SubjectPublicKeyInfo 1031 | ) == FALSE; 1032 | } 1033 | 1034 | // Getting Signer Certificate Information. 1035 | BOOL GetSignerCertificateInfo( 1036 | LPCWSTR FileName, 1037 | std::list & SignChain 1038 | ) { 1039 | BOOL bSucceed = FALSE; 1040 | BOOL bReturn = FALSE; 1041 | HCERTSTORE hSystemStore = NULL; 1042 | SIGNDATA_HANDLE AuthSignData = { 0 }; 1043 | std::list SignDataChain; 1044 | 1045 | SignChain.clear(); 1046 | // Open system certstore handle, in order to find root certificate. 1047 | hSystemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, MY_ENCODING, 1048 | NULL, 1049 | CERT_SYSTEM_STORE_CURRENT_USER, 1050 | L"Root" 1051 | ); 1052 | if (!hSystemStore) 1053 | { 1054 | INT error = GetLastError(); 1055 | return FALSE; 1056 | } 1057 | // Query file auth signature and cert store Object. 1058 | HCRYPTMSG hAuthCryptMsg = NULL; 1059 | DWORD dwEncoding = 0x00; 1060 | bReturn = CryptQueryObject(CERT_QUERY_OBJECT_FILE, FileName, 1061 | CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, 1062 | CERT_QUERY_FORMAT_FLAG_BINARY, 1063 | 0, 1064 | &dwEncoding, 1065 | NULL, 1066 | NULL, 1067 | &AuthSignData.hCertStoreHandle, 1068 | &hAuthCryptMsg, 1069 | NULL 1070 | ); 1071 | if (!bReturn) 1072 | { 1073 | INT error = GetLastError(); 1074 | CertCloseStore(hSystemStore, 0); 1075 | return FALSE; 1076 | } 1077 | // Get signer information pointer. 1078 | bReturn = MyCryptMsgGetParam(hAuthCryptMsg, CMSG_SIGNER_INFO_PARAM, 1079 | 0, 1080 | (PVOID *)&AuthSignData.pSignerInfo, 1081 | &AuthSignData.dwObjSize 1082 | ); 1083 | CryptMsgClose(hAuthCryptMsg); 1084 | hAuthCryptMsg = NULL; 1085 | if (!bReturn) 1086 | { 1087 | INT error = GetLastError(); 1088 | CertCloseStore(AuthSignData.hCertStoreHandle, 0); 1089 | CertCloseStore(hSystemStore, 0); 1090 | return FALSE; 1091 | } 1092 | 1093 | // Get and append nested signature information. 1094 | SignDataChain.push_back(AuthSignData); 1095 | bReturn = GetNestedSignerInfo(&AuthSignData, SignDataChain); 1096 | 1097 | list::iterator iter = SignDataChain.begin(); 1098 | for (; iter != SignDataChain.end(); iter++) 1099 | { 1100 | PCCERT_CONTEXT pOrigContext = NULL; 1101 | PCCERT_CONTEXT pCurrContext = NULL; 1102 | LPCSTR szObjId = NULL; 1103 | PCMSG_SIGNER_INFO pCounterSigner = NULL; 1104 | SIGN_NODE_INFO SignNode; 1105 | 1106 | GetAuthedAttribute(iter->pSignerInfo); 1107 | // Get signature timestamp. 1108 | GetCounterSignerInfo(iter->pSignerInfo, &pCounterSigner); 1109 | if (pCounterSigner) 1110 | { 1111 | bReturn = GetCounterSignerData(pCounterSigner, SignNode.CounterSign); 1112 | } 1113 | else 1114 | { 1115 | bReturn = GetGeneralizedTimeStamp(iter->pSignerInfo, 1116 | SignNode.CounterSign.TimeStamp 1117 | ); 1118 | } 1119 | // Get digest algorithm. 1120 | szObjId = iter->pSignerInfo->HashAlgorithm.pszObjId; 1121 | bReturn = CalculateDigestAlgorithm(szObjId, SignNode.DigestAlgorithm); 1122 | // Get signature version. 1123 | bReturn = CalculateSignVersion(iter->pSignerInfo->dwVersion, SignNode.Version); 1124 | // Find the first certificate Context information. 1125 | pCurrContext = CertFindCertificateInStore(iter->hCertStoreHandle, 1126 | MY_ENCODING, 1127 | 0, 1128 | CERT_FIND_ISSUER_NAME, 1129 | (PVOID)&iter->pSignerInfo->Issuer, 1130 | NULL 1131 | ); 1132 | bReturn = (pCurrContext != NULL); 1133 | while (bReturn) 1134 | { 1135 | pOrigContext = pCurrContext; 1136 | // Get every signer signature information. 1137 | bReturn = GetSignerSignatureInfo(hSystemStore, iter->hCertStoreHandle, 1138 | pOrigContext, 1139 | pCurrContext, 1140 | SignNode 1141 | ); 1142 | CertFreeCertificateContext(pOrigContext); 1143 | } 1144 | if (pCurrContext) CertFreeCertificateContext(pCurrContext); 1145 | if (pCounterSigner) LocalFree(pCounterSigner); 1146 | if (iter->pSignerInfo) LocalFree(iter->pSignerInfo); 1147 | if (iter->hCertStoreHandle) CertCloseStore(iter->hCertStoreHandle, 0); 1148 | bSucceed = TRUE; 1149 | SignChain.push_back(SignNode); 1150 | } 1151 | CertCloseStore(hSystemStore, 0); 1152 | return bSucceed; 1153 | } 1154 | 1155 | BOOL MyCryptCalcFileHash( 1156 | HANDLE FileHandle, 1157 | PBYTE *szBuffer, 1158 | DWORD *HashSize 1159 | ) { 1160 | BOOL bReturn = FALSE; 1161 | if (!szBuffer || !HashSize) 1162 | { 1163 | return FALSE; 1164 | } 1165 | *HashSize = 0x00; 1166 | // Get size. 1167 | bReturn = CryptCATAdminCalcHashFromFileHandle(FileHandle, HashSize, NULL, 0x00); 1168 | if (0 == *HashSize) // HashSize being zero means fatal error. 1169 | { 1170 | return FALSE; 1171 | } 1172 | *szBuffer = (PBYTE)calloc(*HashSize, 1); 1173 | bReturn = CryptCATAdminCalcHashFromFileHandle(FileHandle, HashSize, *szBuffer, 0x00); 1174 | if (!bReturn) 1175 | { 1176 | free(*szBuffer); 1177 | } 1178 | return bReturn; 1179 | } 1180 | 1181 | BOOL CheckFileDigitalSignature( 1182 | LPCWSTR FilePath, 1183 | LPCWSTR CataPath, 1184 | std::wstring & CataFile, 1185 | std::string & SignType, 1186 | std::list & SignChain 1187 | ) { 1188 | PVOID Context = NULL; 1189 | BOOL bReturn = FALSE; 1190 | 1191 | CataFile = CataPath ? CataPath : L""; 1192 | SignType = "embedded"; 1193 | 1194 | do 1195 | { 1196 | // Skip getting catalog Context if CataPath is specified. 1197 | if (CataPath) 1198 | { 1199 | break; 1200 | } 1201 | // Acquire signature Context structure. 1202 | bReturn = CryptCATAdminAcquireContext(&Context, NULL, 0); 1203 | if (!bReturn) 1204 | { 1205 | break; 1206 | } 1207 | // Open the specified file handle to get the file hash. 1208 | HANDLE FileHandle = CreateFileW(FilePath, GENERIC_READ, 1209 | 7, 1210 | NULL, 1211 | OPEN_EXISTING, 1212 | FILE_FLAG_BACKUP_SEMANTICS, 1213 | NULL 1214 | ); 1215 | if (INVALID_HANDLE_VALUE == FileHandle) 1216 | { 1217 | break; 1218 | } 1219 | // Calculate file hash. 1220 | DWORD dwHashSize = 0x00; 1221 | PBYTE szBuffer = NULL; 1222 | bReturn = MyCryptCalcFileHash(FileHandle, &szBuffer, &dwHashSize); 1223 | CloseHandle(FileHandle); 1224 | if (!bReturn) 1225 | { 1226 | break; 1227 | } 1228 | // Get catalog Context structure. 1229 | UINT uiCataLimit = 0x00; 1230 | HCATINFO CataContext = NULL; 1231 | do 1232 | { 1233 | // Probe catalog Context structure layer. 1234 | CataContext = CryptCATAdminEnumCatalogFromHash(Context, 1235 | szBuffer, 1236 | dwHashSize, 1237 | 0, 1238 | uiCataLimit == 0 ? NULL : &CataContext 1239 | ); 1240 | uiCataLimit++; 1241 | } while (CataContext); 1242 | uiCataLimit--; 1243 | for (UINT uiIter = 0; uiIter < uiCataLimit; uiIter++) 1244 | { 1245 | // Get specified catalog Context structure. 1246 | CataContext = CryptCATAdminEnumCatalogFromHash(Context, 1247 | szBuffer, 1248 | dwHashSize, 1249 | 0, 1250 | &CataContext 1251 | ); 1252 | } 1253 | free(szBuffer); 1254 | if (!CataContext) 1255 | { 1256 | break; 1257 | } 1258 | // Get catalog information. 1259 | CATALOG_INFO CataInfo = { 0 }; 1260 | CataInfo.cbStruct = sizeof(CATALOG_INFO); 1261 | bReturn = CryptCATCatalogInfoFromContext(CataContext, &CataInfo, 0); 1262 | if (bReturn) 1263 | { 1264 | CataFile = CataInfo.wszCatalogFile; 1265 | } 1266 | // Release catalog Context structure. 1267 | bReturn = CryptCATAdminReleaseCatalogContext(Context, CataContext, 0); 1268 | CataContext = NULL; 1269 | } while (FALSE); 1270 | if (Context) 1271 | { 1272 | // Release signature Context structure. 1273 | bReturn = CryptCATAdminReleaseContext(Context, 0); 1274 | Context = NULL; 1275 | } 1276 | 1277 | // Get certificate information. 1278 | bReturn = GetSignerCertificateInfo(FilePath, SignChain); 1279 | if (!bReturn && !CataFile.empty()) 1280 | { 1281 | // If we cannot get embedded signature information, we 1282 | // just attempt to get cataloged signature information 1283 | // if it has catalog or catalog is specified. 1284 | SignType = "cataloged"; 1285 | bReturn = GetSignerCertificateInfo(CataFile.c_str(), SignChain); 1286 | } 1287 | return bReturn; 1288 | } 1289 | 1290 | INT wmain(INT argc, WCHAR *argv[]) 1291 | { 1292 | if (argc != 2) 1293 | { 1294 | std::cout << "Parameter error!" << endl; 1295 | std::cout << "Usage: PESignAnalyzer.exe filepath" << endl; 1296 | return 0x01; 1297 | } 1298 | 1299 | BOOL bReturn = FALSE; 1300 | PWCHAR pwzFilePath = NULL; 1301 | std::wstring CataFile; 1302 | std::string SignType; 1303 | std::list SignChain; 1304 | 1305 | pwzFilePath = argv[1]; 1306 | std::wcout << L"filepath: " << pwzFilePath << endl; 1307 | bReturn = CheckFileDigitalSignature(pwzFilePath, NULL, CataFile, SignType, SignChain); 1308 | if (!bReturn) 1309 | { 1310 | std::cout << "signtype: " << "none" << endl; 1311 | return 0x01; 1312 | } 1313 | std::cout << "signtype: " << SignType << endl; 1314 | std::wcout << L"catafile: " << CataFile << endl; 1315 | std::cout << "-----------------------" << endl; 1316 | UINT idx = 0; 1317 | std::list::iterator iter = SignChain.begin(); 1318 | for (; iter != SignChain.end(); iter++) 1319 | { 1320 | std::cout << "[ The " << ++idx << " Sign Info ]" << endl; 1321 | std::cout << "timestamp: " << iter->CounterSign.TimeStamp << endl; 1322 | std::cout << "version: " << iter->Version << endl; 1323 | std::cout << "digestAlgorithm: " << iter->DigestAlgorithm << endl; 1324 | 1325 | std::list::iterator iter1 = iter->CertChain.begin(); 1326 | for (; iter1 != iter->CertChain.end(); iter1++) 1327 | { 1328 | std::cout << " |--" << "-------------------" << endl; 1329 | std::cout << " |- " << "subject: " << iter1->SubjectName << endl; 1330 | std::cout << " |- " << "issuer: " << iter1->IssuerName << endl; 1331 | std::cout << " |- " << "serial: " << iter1->Serial << endl; 1332 | std::cout << " |- " << "thumbprint: " << iter1->Thumbprint << endl; 1333 | std::cout << " |- " << "signAlgorithm: " << iter1->SignAlgorithm << endl; 1334 | std::cout << " |- " << "version: " << iter1->Version << endl; 1335 | std::cout << " |- " << "notbefore: " << iter1->NotBefore << endl; 1336 | std::cout << " |- " << "notafter: " << iter1->NotAfter << endl; 1337 | std::wcout << L" |- " << L"CRLpoint: " << iter1->CRLpoint << endl; 1338 | } 1339 | std::cout << "-----------------------" << endl; 1340 | } 1341 | return 0x00; 1342 | } 1343 | --------------------------------------------------------------------------------