summaryrefslogtreecommitdiff
path: root/peversion
blob: b41ff6a856961fd07af12d44eae1b1c208fd3791 (plain)
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/usr/bin/python

import struct
import os
import sys
import string
import datetime
import re

#### util

def oldness(f):
    if not os.path.exists(f):
        return 'file doesn\'t exist'
    d = datetime.datetime.now() - datetime.datetime.fromtimestamp(os.stat(f).st_mtime)
    if d.days > 0:
        s = '%d days' % d.days
    elif d.seconds > 3600:
        s = '%d hours' % int(d.seconds / 3600)
    elif d.seconds > 60:
        s = '%d minutes' % int(d.seconds / 60)
    s = '%d seconds' % d.seconds
    return s, d.seconds

def get_lib(fname, rexp):
    l = os.popen('ldd "%s"' % fname).readlines()
    refs_raw=[x.split('=>') for x in l if '=>' in x]
    refs=[(r[0].strip().split()[0], r[1].strip().split()[0]) for r in refs_raw]
    ref = [r[1] for r in refs if re.match(rexp, r[0])]
    if len(ref) == 0:
        return 'library not found'
    return ref[0]

#### small resource parsing library
"""
typedef struct {
  DWORD DataSize;
  DWORD HeaderSize;
  DWORD TYPE;
  DWORD NAME;
  DWORD DataVersion;
  WORD  MemoryFlags;
  WORD  LanguageId;
  DWORD Version;
  DWORD Characteristics;
} RESOURCEHEADER;
"""

def parse_resource_proper(rsrc):
    # http://www.fine-view.com/jp/labs/doc/res32fmt.htm
    # TODO
    off = 0
    def get(d):
        l = struct.calcsize(d)
        return off + l, struct.unpack(d, rsrc[off:off+l])
    # data_size - extra space following header, not including padding
    # header_size - size of header structure
    # res_type - resource type id (standard or custom)
    off, (data_size, header_size) = get('<LL')
    return {}
    (data_size, header_size, res_type, res_name, data_version,
     memory_flags, language_id, version, characteristics
     ) = struct.unpack(header_def, rsrc[:header_len])
    print data_size, header_size, res_type, res_name, data_version
    return locals()

printable = set(string.printable)

def looks_like_a_string_key(key):
    for key_i in xrange(1, len(key) - 1):
        key_subset = set(key[key_i:])
        if key_subset <= printable and key[key_i-1] == '\x01':
            return key_i
    return None

def resource_to_strings(rsrc):
    """
    1. split to repeating non zero two byte groups (i.e. utf-16 strings)
    2. throw anything of length 1 or 0 (fixes some misses with keys, particularily
    CompanyName)
    3. go over anything left: a key looks like GGG\x01KKKKKK where G is garbage, K
    is an ascii (so in printable). Check bruteforcely. The value is always right after
    it.
    """
    groups = [[]]
    for u in [rsrc[x:x+2] for x in xrange(0,len(rsrc),2)]:
        if u == '\x00\x00':
            groups.append([])
            continue
        groups[-1].append(u)
    strings = [''.join([a[0] for a in g]) for g in groups if len(g) > 1]
    out = {}
    ind = 0
    #print '\n'.join('%2d: %r' % (i, s) for i, s in enumerate(strings))
    while ind < len(strings) - 1:
        key = strings[ind]
        #print ind, repr(key)
        if key == 'VS_VERSION_INFO':
            out['VS_VERSION_INFO'] = strings[ind+1]
            ind += 2
            continue
        if set(key) & printable == set():
            ind += 1
            continue
        key_i = looks_like_a_string_key(key)
        if key_i:
            out[key[key_i:]] = strings[ind + 1]
            ind += 2
            continue
        ind += 1
    return out

parse_resource = resource_to_strings
####

try:
    import pefile
except:
    if os.path.exists('/usr/bin/yum'):
        print "please do 'yum install python-pefile'"
    else:
        print "missing python pefile module"
    raise SystemExit

if __name__ == '__main__':
    f=sys.argv[-1]
    if not os.path.exists(f):
        print "%s doesn't exist" % f
        raise SystemExit
    try:
        pe=pefile.PE(f)
    except:
        print "can't parse pe header - is this a PE file?"
        raise SystemExit
    rsrc = [s for s in pe.sections if s.Name.startswith('.rsrc\x00')]
    if len(rsrc) == 0:
        print "missing .rsrc section, no version information in this file"
        raise SystemExit
    d = parse_resource(rsrc[0].data)
    n = max(map(len, d.keys()))
    #print '\n'.join('%s: %r' % (k.ljust(n), v) for k,v in d.items())
    age_string, age_seconds = oldness(f)
    print '%s: FV: %r, PV: %r\nage: %s' % (f, d['FileVersion'], d['ProductVersion'], age_string)
    if age_seconds < 60:
        sys.exit(0)
    sys.exit(1)