File: copydlls.py

package info (click to toggle)
performous-composer 2.0%2B20181009-gitbeeea23-2.1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 668 kB
  • sloc: cpp: 5,646; python: 153; sh: 42; makefile: 13
file content (116 lines) | stat: -rwxr-xr-x 4,210 bytes parent folder | download | duplicates (7)
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
#!/usr/bin/env python
# DLL dependency resolution and copying script.
# Copyright (C) 2010 John Stumpo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os
import shutil
import struct
import sys

if len(sys.argv) != 3:
    sys.stderr.write('''Usage: %s [source] [destination]
Copies DLLs in source needed by PE executables in destination to destination.
Both source and destination should be directories.
''' % sys.argv[0])
    sys.exit(1)

def is_pe_file(file):
    f = open(file, 'rb')
    if f.read(2) != 'MZ':
        return False  # DOS magic number not present
    f.seek(60)
    peoffset = struct.unpack('<L', f.read(4))[0]
    f.seek(peoffset)
    if f.read(4) != 'PE\0\0':
        return False  # PE magic number not present
    return True

def get_imports(file):
    f = open(file, 'rb')
    # We already know it's a PE, so don't bother checking again.
    f.seek(60)
    pe_header_offset = struct.unpack('<L', f.read(4))[0]

    # Get sizes of tables we need.
    f.seek(pe_header_offset + 6)
    number_of_sections = struct.unpack('<H', f.read(2))[0]
    f.seek(pe_header_offset + 116)
    number_of_data_directory_entries = struct.unpack('<L', f.read(4))[0]
    data_directory_offset = f.tell()  # it's right after the number of entries

    # Where is the import table?
    f.seek(data_directory_offset + 8)
    rva_of_import_table = struct.unpack('<L', f.read(4))[0]

    # Get the section ranges so we can convert RVAs to file offsets.
    f.seek(data_directory_offset + 8 * number_of_data_directory_entries)
    sections = []
    for i in range(number_of_sections):
        section_descriptor_data = f.read(40)
        name, size, va, rawsize, offset = struct.unpack('<8sLLLL', section_descriptor_data[:24])
        sections.append({'min': va, 'max': va+rawsize, 'offset': offset})

    def seek_to_rva(rva):
        for s in sections:
            if s['min'] <= rva and rva < s['max']:
                f.seek(rva - s['min'] + s['offset'])
                return
        raise ValueError, 'Could not find section for RVA.'

    # Walk the import table and get RVAs to the null-terminated names of DLLs this file uses.
    # The table is terminated by an all-zero entry.
    seek_to_rva(rva_of_import_table)
    dll_rvas = []
    while True:
        import_descriptor = f.read(20)
        if import_descriptor == '\0' * 20:
            break
        dll_rvas.append(struct.unpack('<L', import_descriptor[12:16])[0])

    # Read the DLL names from the RVAs we found in the import table.
    dll_names = []
    for rva in dll_rvas:
        seek_to_rva(rva)
        name = ''
        while True:
            c = f.read(1)
            if c == '\0':
                break
            name += c
        dll_names.append(name)

    return dll_names


src_contents = os.listdir(sys.argv[1])
dest_contents = os.listdir(sys.argv[2])
for dest_name in dest_contents:
    dest_fname = os.path.join(sys.argv[2], dest_name)
    if os.path.isfile(dest_fname) and is_pe_file(dest_fname):
        print dest_name
        for dll in get_imports(dest_fname):
            print '- %s' % dll,
            if dll.lower() in [n.lower() for n in dest_contents]:
                print '(already present)'
            else:
                for n in src_contents:
                    if n.lower() == dll.lower():
                        shutil.copyfile(os.path.join(sys.argv[1], n), os.path.join(sys.argv[2], n))
                        dest_contents.append(n)
                        print '(copied)'
                        break
                else:
                    print '(assumed to be provided by operating system)'