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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
module StrSet = Set.Make (String)
type reason = MZSize | MZSig | PESize | PESig | PEOptSize | PEOptSig
exception Exit
let = 0x40
let = 0x18
let = 0x60
let = 0x70
let = 0x28
let import_descriptor_size = 0x14
let mz_magic = 0x4D5A
let pe_magic = 0x50450000l
let pe32_magic = 0x0B01
let pe32_plus_magic = 0x0B02
let mz_lfanew = 0x3C
let pe_number_of_sections = 0x06
let = 0x14
let pe32_number_of_rva_and_sizes = 0x5C
let pe32_plus_number_of_rva_and_sizes = 0x6C
let dd_imports_rva = 0x08
let id_name = 0x0C
let read_string =
let buf = Buffer.create 64 in
fun ic ->
let rec aux () =
match input_char ic with
| '\000' -> Buffer.contents buf
| c ->
Buffer.add_char buf c;
aux ()
in
Buffer.reset buf;
aux ()
let rva_to_address sect_hdr rva =
let sh_opt =
List.find_opt (fun (vs, va, _ds, _da) ->
rva >= va && rva < va + vs
) sect_hdr
in
match sh_opt with
| Some (_vs, va, _ds, da) ->
da + (rva - va)
| None ->
0
let get_dlls_ic ic =
let = Bytes.create mz_header_size in
begin
try really_input ic mz_header 0 mz_header_size
with End_of_file -> raise (InvalidHeader MZSize)
end;
let mz_sig = Bytes.get_uint16_be mz_header 0 in
if mz_sig <> mz_magic then
raise (InvalidHeader MZSig);
let pe_address = Bytes.get_int32_le mz_header mz_lfanew |> Int32.to_int in
seek_in ic pe_address;
let = Bytes.create pe_header_size in
begin
try really_input ic pe_header 0 pe_header_size
with End_of_file -> raise (InvalidHeader PESize)
end;
let pe_sig = Bytes.get_int32_be pe_header 0 in
if pe_sig <> pe_magic then
raise (InvalidHeader PESig);
let nb_sections = Bytes.get_uint16_le pe_header pe_number_of_sections in
let size_opt_hdr = Bytes.get_uint16_le pe_header pe_size_of_optional_header in
if size_opt_hdr = 0 then
raise Exit;
seek_in ic (pe_address + pe_header_size);
let = Bytes.create size_opt_hdr in
begin
try really_input ic pe_opt_header 0 size_opt_hdr
with End_of_file -> raise (InvalidHeader PEOptSize)
end;
let pe32_sig = Bytes.get_uint16_be pe_opt_header 0 in
let nb_rva_sizes_offset, data_dir_offset =
if pe32_sig = pe32_magic then
pe32_number_of_rva_and_sizes, pe32_header_size
else if pe32_sig = pe32_plus_magic then
pe32_plus_number_of_rva_and_sizes, pe32_plus_header_size
else
raise (InvalidHeader PEOptSig);
in
let nb_rva_sizes =
Bytes.get_int32_le pe_opt_header nb_rva_sizes_offset |> Int32.to_int in
if nb_rva_sizes < 2 then
raise Exit;
let imports_rva =
Bytes.get_int32_le pe_opt_header (data_dir_offset + dd_imports_rva)
|> Int32.to_int in
seek_in ic (pe_address + pe_header_size + size_opt_hdr);
let sh = Bytes.create section_header_size in
let rec aux i sect_hdrs =
if i >= nb_sections then
List.rev sect_hdrs
else
begin
really_input ic sh 0 section_header_size;
let virt_size = Bytes.get_int32_le sh 8 |> Int32.to_int in
let virt_address = Bytes.get_int32_le sh 12 |> Int32.to_int in
let data_size = Bytes.get_int32_le sh 16 |> Int32.to_int in
let data_address = Bytes.get_int32_le sh 20 |> Int32.to_int in
let sect_hdrs =
(virt_size, virt_address, data_size, data_address) :: sect_hdrs in
aux (i + 1) sect_hdrs
end
in
let sect_hdrs = aux 0 [] in
let imports_address = rva_to_address sect_hdrs imports_rva in
let id = Bytes.create import_descriptor_size in
let rec aux i names =
seek_in ic (imports_address + i * import_descriptor_size);
really_input ic id 0 import_descriptor_size;
let name_rva = Bytes.get_int32_le id id_name |> Int32.to_int in
if name_rva = 0 then
names
else
let name_address = rva_to_address sect_hdrs name_rva in
seek_in ic name_address;
let name = read_string ic in
aux (i + 1) (name :: names)
in
let names = aux 0 [] in
names
let get_dlls_t binary =
let ic = open_in_bin binary in
let dlls =
try get_dlls_ic ic
with
| Exit | InvalidHeader _ -> []
| e -> close_in ic; raise e
in
close_in ic;
dlls
external get_windows_directory : unit -> string = "ml_get_windows_directory"
let is_system32 =
let win_dir = get_windows_directory () in
fun path ->
let prefix =
try String.sub path 0 (String.length win_dir)
with _ -> ""
in
if prefix <> win_dir then false
else
let suffix =
try String.sub path (String.length win_dir)
(String.length path - String.length win_dir)
with _ -> ""
in
match String.split_on_char '\\' suffix with
| directory :: _ ->
String.lowercase_ascii directory = "system32"
|| String.lowercase_ascii directory = "syswow64"
| _ -> false
external resolve_dll : string -> string option = "ml_resolve_dll"
let get_dlls binary =
let rec aux dlls binary =
let binary_dlls = get_dlls_t binary in
let new_dlls =
List.filter_map (fun dll ->
match resolve_dll dll with
| None -> None
| Some (dll) ->
if is_system32 dll then None
else if StrSet.mem dll dlls then None
else Some (dll)
) binary_dlls
in
let dlls =
List.fold_left (fun dlls dll ->
StrSet.add dll dlls
) dlls new_dlls
in
List.fold_left aux dlls new_dlls
in
let dlls = aux StrSet.empty (OpamFilename.to_string binary) in
StrSet.fold (fun dll dlls ->
OpamFilename.of_string (System.normalize_path dll) :: dlls
) dlls [] |> List.rev