├── LICENSE ├── Makefile ├── README.ja.md ├── README.md └── extract-authenticode.c /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (C) 2014, 2019 Masamichi Hosoda. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for nmake 2 | # `Microsoft Visual Studio Express 2013 for Windows Desktop' 3 | # etc. 4 | 5 | CC=cl 6 | CFLAGS= 7 | 8 | extract-authenticode.exe: extract-authenticode.c 9 | $(CC) $(CFLAGS) /Fe$@ $** 10 | clean: 11 | -del *~ 12 | -del *.obj 13 | -------------------------------------------------------------------------------- /README.ja.md: -------------------------------------------------------------------------------- 1 | 2 | # Authenticode署名データ抽出ツール 3 | 4 | [ Japanese (日本語) / [English](./README.md) ] 5 | 6 | [ 7 | https://github.com/trueroad/extract-authenticode 8 | ](https://github.com/trueroad/extract-authenticode) 9 | 10 | PEフォーマットのファイル(.exe や .dll 等)から 11 | Authenticodeの署名データを抽出します。 12 | 13 | ## 必要なもの 14 | 15 | ビルドには以下が必要です。 16 | 17 | - Microsoft Visual Studio Express 2013 for Windows Desktop 18 | - MinGW-W64 19 | 20 | 等 21 | 22 | ## ビルド 23 | 24 | ### Visual Studio 25 | 26 | VS2013 のコマンドプロンプトを開き、 27 | ソースのあるフォルダをカレントフォルダにしてから、 28 | "nmake"を実行すると、"extract-authenticode.exe" が得られます。 29 | 30 | ### MinGW-W64 31 | 32 | ``` 33 | gcc -municode -o extract-authenticode.exe extract-authenticode.c 34 | ``` 35 | 36 | ## 使い方 37 | 38 | 例:explorer.exe の Authenticode データを抽出してみる。 39 | 40 | ``` 41 | extract-authenticode C:\Windows\explorer.exe out.bin 42 | ``` 43 | 44 | 例:抽出された出力ファイルを解析してみる。(openssl が必要です) 45 | 46 | ``` 47 | openssl asn1parse -in out.bin -inform der -i 48 | ``` 49 | 50 | ## おまけ 51 | 52 | Microsoft Authenticodeのタイムスタンプについてお勉強(第2回) 53 | 54 | http://blog.livedoor.jp/k_urushima/archives/729378.html 55 | 56 | に、Authenticode 署名の構造が載っていて、一応の取り出し方が書いてある。 57 | しかし、残念ながらこの方法では Authenticode 署名を 58 | 見つけることができないケースが散見されたので、Microsoft の文献等をあたり、 59 | 正しい取り出し方を調べて実装してみたのが、このツールである。 60 | 61 | 上記ページでは実行プログラムや DLL の末尾の方で、 62 | 63 | ?0 0? 00 00 00 02 02 00-30 82 ... 64 | 65 | の並びが出てきたら、30 82 から最後の部分までが署名データである、 66 | としているが、文献によると、この部分は WIN_CERTIFICATE 構造体であり、 67 | 最初の 4 バイトは構造体のサイズを格納する場所であった。 68 | リトルエンディアンなので、構造体のサイズが 0x00000??0 であれば、 69 | 上記の並びに一致するわけだ。 70 | 71 | 古めの署名ツールではアラインメントの関係で下一桁が 0 になるよう 72 | パディングされていたのかもしれないが、 73 | 新しめの署名ツールでは 0 になるとは限らないようだ。 74 | また、サイズが大きくなっていて 0xff0 を超えてしまうケースもある。 75 | 特に、タイムスタンプの形式が変わっているようで、 76 | 古いタイムスタンプの形式(非 RFC 3161)では 0xff0 に収まっていても、 77 | 新しいタイムスタンプの形式(RFC 3161)では収まらなかったり、などなど、 78 | あるようにも見える。 79 | 80 | ## ライセンス 81 | 82 | Copyright (C) 2014, 2019 Masamichi Hosoda. All rights reserved. 83 | 84 | License: BSD-2-Clause 85 | 86 | [LICENSE](./LICENSE) をご覧ください。 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Extract Authenticode signature data from PE format file 2 | 3 | [ [Japanese (日本語)](./README.ja.md) / English ] 4 | 5 | [ 6 | https://github.com/trueroad/extract-authenticode 7 | ](https://github.com/trueroad/extract-authenticode) 8 | 9 | This tool can extract Authenticode signature data from PE format file. 10 | (e.g. .exe, .dll etc.) 11 | 12 | ## Required 13 | 14 | For building this tool, 15 | 16 | - Microsoft Visual Studio Express 2013 for Windows Desktop 17 | - MinGW-W64 18 | 19 | etc. 20 | 21 | ## Build 22 | 23 | ### Visual Studio 24 | 25 | You run "nmake" to get "extract-authenticode.exe". 26 | 27 | ### MinGW-W64 28 | 29 | ``` 30 | gcc -municode -o extract-authenticode.exe extract-authenticode.c 31 | ``` 32 | 33 | ## Usage 34 | 35 | Example. To extract Authenticode data of explorer.exe, 36 | 37 | ``` 38 | extract-authenticode C:\Windows\explorer.exe out.bin 39 | ``` 40 | 41 | Example. To parse output file (required openssl), 42 | 43 | ``` 44 | openssl asn1parse -in out.bin -inform der -i 45 | ``` 46 | 47 | ## License 48 | 49 | Copyright (C) 2014, 2019 Masamichi Hosoda. All rights reserved. 50 | 51 | License: BSD-2-Clause 52 | 53 | See [LICENSE](./LICENSE). 54 | -------------------------------------------------------------------------------- /extract-authenticode.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Extract Authenticode signature data from PE format file 4 | 5 | Copyright (C) 2014 Masamichi Hosoda. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #ifndef UNICODE 31 | #define UNICODE 32 | #endif 33 | #ifndef _UNICODE 34 | #define _UNICODE 35 | #endif 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #define SIZEOF_WIN_CERTIFICATE_HDR 8 46 | 47 | DWORD parse(FILE *fin); 48 | int parse_dos_header(FILE *fin); 49 | DWORD parse_nt_header(FILE *fin); 50 | DWORD parse_datadirectory(FILE *fin, PIMAGE_DATA_DIRECTORY pdd); 51 | DWORD parse_authenticode_signature(FILE *fin); 52 | int copy_file(FILE *fin, FILE *fout, DWORD size); 53 | 54 | int wmain(int argc, wchar_t *argv[]) 55 | { 56 | FILE *fin, *fout; 57 | DWORD size; 58 | int retval; 59 | 60 | _wsetlocale(LC_ALL, L""); 61 | 62 | wprintf(L"Extract Authenticode signature data from PE format file\n" 63 | L"Copyright (C) 2014 Masamichi Hosoda. All rights reserved.\n\n"); 64 | 65 | if(argc!=3) 66 | { 67 | fwprintf(stderr, 68 | L"usage: extract-authenticode [input output]\n\n" 69 | L" input: Authenticode signed PE format filename.\n" 70 | L" (e.g. .exe, .dll, etc.)\n" 71 | L" output: PKCS#7 signed data filename.\n"); 72 | return 1; 73 | } 74 | 75 | fin=_wfopen(argv[1], L"rb"); 76 | if(!fin) 77 | { 78 | fwprintf(stderr, L"input file %s open failed: %s\n", argv[1], 79 | _wcserror(errno)); 80 | return 1; 81 | } 82 | 83 | size=parse(fin); 84 | if(!size) 85 | { 86 | fclose(fin); 87 | return 1; 88 | } 89 | 90 | fout=_wfopen(argv[2], L"wb"); 91 | if(!fout) 92 | { 93 | fwprintf(stderr, L"output file %s open failed: %s\n", argv[2], 94 | _wcserror(errno)); 95 | fclose(fin); 96 | return 1; 97 | } 98 | 99 | retval=copy_file(fin, fout, size); 100 | 101 | fclose(fout); 102 | fclose(fin); 103 | 104 | return retval; 105 | } 106 | 107 | DWORD parse(FILE *fin) 108 | { 109 | DWORD size, length; 110 | 111 | if(parse_dos_header(fin)) 112 | { 113 | return 0; 114 | } 115 | 116 | size=parse_nt_header(fin); 117 | 118 | if(size) 119 | { 120 | length=parse_authenticode_signature(fin); 121 | if(size>length) 122 | { 123 | return length; 124 | } 125 | else 126 | { 127 | return 0; 128 | } 129 | } 130 | return 0; 131 | } 132 | 133 | int parse_dos_header(FILE *fin) 134 | { 135 | IMAGE_DOS_HEADER idh; 136 | size_t rc; 137 | fpos_t pos; 138 | int retval; 139 | 140 | rc=fread(&idh, sizeof(idh), 1, fin); 141 | if(rc<0) 142 | { 143 | _wperror(L"fread IMAGE_DOS_HEADER failed"); 144 | return 1; 145 | } 146 | else if(rc==0) 147 | { 148 | wprintf(L"fread IMAGE_DOS_HEADER failed: EOF\n"); 149 | return 1; 150 | } 151 | 152 | if(idh.e_magic != IMAGE_DOS_SIGNATURE) 153 | { 154 | wprintf(L"IMAGE_DOS_HEADER e_magic = unknown (0x%04x)\n", 155 | idh.e_magic); 156 | return 1; 157 | } 158 | 159 | wprintf(L"IMAGE_DOS_HEADER e_lfanew = 0x%08x\n", idh.e_lfanew); 160 | 161 | pos=idh.e_lfanew; 162 | retval=fsetpos(fin, &pos); 163 | if(retval!=0) 164 | { 165 | _wperror(L"fsetpos IMAGE_DOS_HEADER e_lfanew failed"); 166 | return 1; 167 | } 168 | return 0; 169 | } 170 | 171 | DWORD parse_nt_header(FILE *fin) 172 | { 173 | union 174 | { 175 | IMAGE_NT_HEADERS32 inh32; 176 | IMAGE_NT_HEADERS64 inh64; 177 | } inh; 178 | size_t rc; 179 | 180 | rc=fread(&inh, sizeof(inh), 1, fin); 181 | if(rc<0) 182 | { 183 | _wperror(L"fread IMAGE_NT_HEADERS failed"); 184 | return 0; 185 | } 186 | else if(rc==0) 187 | { 188 | wprintf(L"fread IMAGE_NT_HEADER failed: EOF\n"); 189 | return 0; 190 | } 191 | 192 | if(inh.inh32.Signature != IMAGE_NT_SIGNATURE) 193 | { 194 | wprintf(L"IMAGE_NT_HEADERS Signature = unknown (0x%08x)\n", 195 | inh.inh32.Signature); 196 | return 0; 197 | } 198 | 199 | if(inh.inh32.FileHeader.Machine == IMAGE_FILE_MACHINE_I386) 200 | { 201 | wprintf(L"IMAGE_FILE_HEADER Machine = IMAGE_FILE_MACHINE_I386\n"); 202 | } 203 | else if(inh.inh64.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64) 204 | { 205 | wprintf(L"IMAGE_FILE_HEADER Machine = IMAGE_FILE_MACHINE_AMD64\n"); 206 | } 207 | else 208 | { 209 | wprintf(L"IMAGE_FILE_HEADER Machine = unknown (0x%04x)\n", 210 | inh.inh32.FileHeader.Machine); 211 | } 212 | 213 | if(inh.inh32.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) 214 | { 215 | wprintf(L"IMAGE_OPTIONAL_HEADER32 Magic found.\n"); 216 | return parse_datadirectory(fin, inh.inh32.OptionalHeader.DataDirectory); 217 | } 218 | else if(inh.inh64.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) 219 | { 220 | wprintf(L"IMAGE_OPTIONAL_HEADER64 Magic found.\n"); 221 | return parse_datadirectory(fin, inh.inh64.OptionalHeader.DataDirectory); 222 | } 223 | 224 | wprintf(L"IMAGE_OPTIONAL_HEADER Magic = unknown (0x%04x)\n", 225 | inh.inh32.OptionalHeader.Magic); 226 | return 0; 227 | } 228 | 229 | DWORD parse_datadirectory(FILE *fin, PIMAGE_DATA_DIRECTORY pdd) 230 | { 231 | fpos_t pos; 232 | DWORD size; 233 | int retval; 234 | 235 | pos=pdd[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress; 236 | size=pdd[IMAGE_DIRECTORY_ENTRY_SECURITY].Size; 237 | 238 | wprintf(L"IMAGE_DIRECTORY_ENTRY_SECURITY VirtualAddress = 0x%08x\n", 239 | pos); 240 | wprintf(L"IMAGE_DIRECTORY_ENTRY_SECURITY Size = 0x%08x\n", 241 | size); 242 | 243 | if(pos && size) 244 | { 245 | retval=fsetpos(fin, &pos); 246 | if(retval!=0) 247 | { 248 | _wperror(L"fsetpos IMAGE_DIRECTORY_ENTRY_SECURITY " 249 | L"VirtualAddress failed"); 250 | return 0; 251 | } 252 | return size; 253 | } 254 | wprintf(L"WIN_CERTIFICATE not found.\n"); 255 | return 0; 256 | } 257 | 258 | DWORD parse_authenticode_signature(FILE *fin) 259 | { 260 | size_t rc; 261 | WIN_CERTIFICATE wc; 262 | 263 | rc=fread(&wc, SIZEOF_WIN_CERTIFICATE_HDR, 1, fin); 264 | if(rc<0) 265 | { 266 | _wperror(L"fread WIN_CERTIFICATE failed"); 267 | return 0; 268 | } 269 | else if(rc==0) 270 | { 271 | wprintf(L"fread WIN_CERTIFICATE failed: EOF\n"); 272 | return 0; 273 | } 274 | 275 | wprintf(L"WIN_CERTIFICATE dwLength = 0x%08x\n", wc.dwLength); 276 | 277 | switch(wc.wRevision) 278 | { 279 | case WIN_CERT_REVISION_1_0: 280 | wprintf(L"WIN_CERTIFICATE wRevision = WIN_CERT_REVISION_1_0\n"); 281 | break; 282 | case WIN_CERT_REVISION_2_0: 283 | wprintf(L"WIN_CERTIFICATE wRevision = WIN_CERT_REVISION_2_0\n"); 284 | break; 285 | default: 286 | wprintf(L"WIN_CERTIFICATE wRevision = unknown (0x%04x)\n", 287 | wc.wRevision); 288 | break; 289 | } 290 | 291 | if(wc.wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) 292 | { 293 | wprintf(L"WIN_CERTIFICATE wCertificateType = " 294 | L"WIN_CERT_TYPE_PKCS_SIGNED_DATA\n"); 295 | } 296 | else 297 | { 298 | wprintf(L"WIN_CERTIFICATE wCertificateType = unknown (0x%04x)\n", 299 | wc.wCertificateType); 300 | } 301 | 302 | return wc.dwLength - SIZEOF_WIN_CERTIFICATE_HDR; 303 | } 304 | 305 | int copy_file(FILE *fin, FILE *fout, DWORD size) 306 | { 307 | unsigned char *buff; 308 | size_t count; 309 | 310 | buff=malloc(size); 311 | if(!buff) 312 | { 313 | _wperror(L"malloc failed"); 314 | return 1; 315 | } 316 | 317 | count=fread(buff, size, 1, fin); 318 | if(count<0) 319 | { 320 | _wperror(L"fread WIN_CERTIFICATE bCertificate failed"); 321 | free(buff); 322 | return 1; 323 | } 324 | else if(count==0) 325 | { 326 | wprintf(L"fread WIN_CERTIFICATE bCertificate failed: EOF\n"); 327 | free(buff); 328 | return 1; 329 | } 330 | 331 | count=fwrite(buff, size, 1, fout); 332 | if(count<0) 333 | { 334 | _wperror(L"fwrite pkcs#7 failed"); 335 | free(buff); 336 | return 1; 337 | } 338 | else if(count==0) 339 | { 340 | wprintf(L"fwrite pkcs#7 failed: EOF\n"); 341 | free(buff); 342 | return 1; 343 | } 344 | 345 | free(buff); 346 | return 0; 347 | } 348 | --------------------------------------------------------------------------------