隅っこのおぼえがきノート

ちょっとメモしたいことをメモしていきます。あまりキレイじゃないのでリファクタリング推奨

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

  1. --/--/--(--) --:--:--|
  2. スポンサー広告

アセンブリ(exe,dll)に埋め込まれているビルド日時のタイムスタンプを取得してみる

少し前に、Windows環境でアセンブリのビルド日時をC#でお手軽に取得する記事を書いたが、別の方法でもとれそうだ。
アセンブリに埋め込まれているビルド日時のタイムスタンプを取り出す方法だ。
この方法なら、バージョンを"1.0.*"みたいにしなくてもいいし、ファイルの作成日時やら更新日時が何かの拍子に変わっても正しいビルド日時を取得することができるはず。
あと、作られてる言語だとかフレームワークとかも関係なく取れる……と思う。

興味本位でやってみた内容なので、参考にされる場合は十分に検証を行うことをオススメします。
環境やモノによっては全く勝手が違うかもしれません。

さて本題。
exeやdllをメモ帳などのテキストエディタで無理矢理開いてみると、先頭の方にエラーメッセージ的なものがある。
"This program cannot be run in DOS mode."
目的のタイムスタンプは、このメッセージよりも後ろのほうにある。たぶん。
もう少し具体的に言うと、4byteのシグネチャ("PE\0\0")の直後にあるCOFFファイル ヘッダの中にタイムスタンプがある。
バイナリエディタがあるなら、「50 45 00 00」("PE\0\0")を検索してみると見つけやすいと思う。

COFFヘッダ等の仕様については、下記ページさんが詳しいです。

S.S'S HOMEPAGE - 逆アセのスス乂 - MSDN技術資料(PE形式の解説)
http://www.interq.or.jp/chubu/r6/reasm/PE_FORMAT/intro.html

3.3 COFFファイル ヘッダ(オブジェクトとイメージ)
http://www.interq.or.jp/chubu/r6/reasm/PE_FORMAT/3_3.html

EXEファイルの内部構造(PEヘッダ)
http://codezine.jp/article/detail/412

んで、このタイムスタンプをC#で取得しようとすると、こんな感じになる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
 
namespace SandBox
{
    public partial class Form12 : Form
    {
        public Form12()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                //string path = "Sandbox.exe";
                string path = this.textBox1.Text;
 
                DateTime buildDateTime = this.GetBuildDateTime(path);
 
                MessageBox.Show(
                    this,
                    string.Format("{0:yyyy'/'MM'/'dd HH':'mm':'ss}", buildDateTime),
                    this.Text,
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Information);
                // 表示結果:"2012/09/11 10:53:19"
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
 
        /// <summary>
        /// アセンブリファイルのビルド日時を取得する。
        /// </summary>
        /// <param name="asmPath">exeやdll等のアセンブリファイルのパス。
        /// <returns>取得したビルド日時。</returns>
        private DateTime GetBuildDateTime(string asmPath)
        {
            // ファイルオープン
            using (FileStream fs = new FileStream(asmPath, FileMode.Open, FileAccess.Read))
            using (BinaryReader br = new BinaryReader(fs))
            {
                // まずはシグネチャを探す
                byte[] signature = { 0x50, 0x45, 0x00, 0x00 };// "PE\0\0"
                List<byte> bytes = new List<byte>();
                while (true)
                {
                    bytes.Add(br.ReadByte());
 
                    if (bytes.Count < signature.Length)
                    {
                        continue;
                    }
 
                    while (signature.Length < bytes.Count)
                    {
                        bytes.RemoveAt(0);
                    }
 
                    bool isMatch = true;
                    for (int i = 0; i < signature.Length; i++)
                    {
                        if (signature[i] != bytes[i])
                        {
                            isMatch = false;
                            break;
                        }
                    }
                    if (isMatch)
                    {
                        break;
                    }
                }
 
                // COFFファイルヘッダを読み取る
                var coff = new
                {
                    Machine = br.ReadBytes(2),
                    NumberOfSections = br.ReadBytes(2),
                    TimeDateStamp = br.ReadBytes(4),
                    PointerToSymbolTable = br.ReadBytes(4),
                    NumberOfSymbols = br.ReadBytes(4),
                    SizeOfOptionalHeader = br.ReadBytes(2),
                    Characteristics = br.ReadBytes(2),
                };
 
                // タイムスタンプをDateTimeに変換
                int timestamp = BitConverter.ToInt32(coff.TimeDateStamp, 0);
                DateTime baseDateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                DateTime buildDateTimeUtc = baseDateTime.AddSeconds(timestamp);
                DateTime buildDateTimeLocal = buildDateTimeUtc.ToLocalTime();
                return buildDateTimeLocal;
            }
        }
    }
}
</byte></byte>


いろんなアセンブリで試してみたけど、試した限りでは全部ビルド日時を取得できた。
でも、頑張って調べてコード書いてみたはいいものの、こんなのフレームワークだとかライブラリで取れそうだな。今更疑問。
まぁいいか。こんな取り方もできるよ!って感じでお送りします。
スポンサーサイト

  1. 2012/09/11(火) 11:07:37|
  2. C#.NET
  3. | トラックバック:0
  4. | コメント:0
<<WMPで[アルバム情報の検索]ができない | ホーム | C++ファイルプロパティの「コンテンツ」?>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://sumikko8note.blog.fc2.com/tb.php/30-0a73c945
この記事にトラックバックする(FC2ブログユーザー)

最新記事

カテゴリ

C#.NET (21)
Visual Studio (17)
ツール (15)
Windows (20)
Linux (4)
その他 (3)
サービス (1)

月別アーカイブ

プロフィール

シカハチ

Author:シカハチ
へぼプログラマです。

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。