OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006, Google Inc. | |
2 // All rights reserved. | |
3 // | |
4 // Redistribution and use in source and binary forms, with or without | |
5 // modification, are permitted provided that the following conditions are | |
6 // met: | |
7 // | |
8 // * Redistributions of source code must retain the above copyright | |
9 // notice, this list of conditions and the following disclaimer. | |
10 // * Redistributions in binary form must reproduce the above | |
11 // copyright notice, this list of conditions and the following disclaimer | |
12 // in the documentation and/or other materials provided with the | |
13 // distribution. | |
14 // * Neither the name of Google Inc. nor the names of its | |
15 // contributors may be used to endorse or promote products derived from | |
16 // this software without specific prior written permission. | |
17 // | |
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | |
30 // macho_id.cc: Functions to gather identifying information from a macho file | |
31 // | |
32 // See macho_id.h for documentation | |
33 // | |
34 // Author: Dan Waylonis | |
35 | |
36 extern "C" { // necessary for Leopard | |
37 #include <fcntl.h> | |
38 #include <mach-o/loader.h> | |
39 #include <mach-o/swap.h> | |
40 #include <openssl/md5.h> | |
41 #include <openssl/sha.h> | |
42 #include <stdio.h> | |
43 #include <stdlib.h> | |
44 #include <string.h> | |
45 #include <sys/time.h> | |
46 #include <sys/types.h> | |
47 #include <unistd.h> | |
48 } | |
49 | |
50 #include "common/mac/macho_id.h" | |
51 #include "common/mac/macho_walker.h" | |
52 #include "common/mac/macho_utilities.h" | |
53 | |
54 namespace MacFileUtilities { | |
55 | |
56 MachoID::MachoID(const char *path) { | |
57 strlcpy(path_, path, sizeof(path_)); | |
58 file_ = open(path, O_RDONLY); | |
59 } | |
60 | |
61 MachoID::~MachoID() { | |
62 if (file_ != -1) | |
63 close(file_); | |
64 } | |
65 | |
66 // The CRC info is from http://en.wikipedia.org/wiki/Adler-32 | |
67 // With optimizations from http://www.zlib.net/ | |
68 | |
69 // The largest prime smaller than 65536 | |
70 #define MOD_ADLER 65521 | |
71 // MAX_BLOCK is the largest n such that 255n(n+1)/2 + (n+1)(MAX_BLOCK-1) <= 2^32
-1 | |
72 #define MAX_BLOCK 5552 | |
73 | |
74 void MachoID::UpdateCRC(unsigned char *bytes, size_t size) { | |
75 // Unrolled loops for summing | |
76 #define DO1(buf,i) {sum1 += (buf)[i]; sum2 += sum1;} | |
77 #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); | |
78 #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); | |
79 #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); | |
80 #define DO16(buf) DO8(buf,0); DO8(buf,8); | |
81 // Split up the crc | |
82 uint32_t sum1 = crc_ & 0xFFFF; | |
83 uint32_t sum2 = (crc_ >> 16) & 0xFFFF; | |
84 | |
85 // Do large blocks | |
86 while (size >= MAX_BLOCK) { | |
87 size -= MAX_BLOCK; | |
88 int block_count = MAX_BLOCK / 16; | |
89 do { | |
90 DO16(bytes); | |
91 bytes += 16; | |
92 } while (--block_count); | |
93 sum1 %= MOD_ADLER; | |
94 sum2 %= MOD_ADLER; | |
95 } | |
96 | |
97 // Do remaining bytes | |
98 if (size) { | |
99 while (size >= 16) { | |
100 size -= 16; | |
101 DO16(bytes); | |
102 bytes += 16; | |
103 } | |
104 while (size--) { | |
105 sum1 += *bytes++; | |
106 sum2 += sum1; | |
107 } | |
108 sum1 %= MOD_ADLER; | |
109 sum2 %= MOD_ADLER; | |
110 crc_ = (sum2 << 16) | sum1; | |
111 } | |
112 } | |
113 | |
114 void MachoID::UpdateMD5(unsigned char *bytes, size_t size) { | |
115 MD5_Update(&md5_context_, bytes, size); | |
116 } | |
117 | |
118 void MachoID::UpdateSHA1(unsigned char *bytes, size_t size) { | |
119 SHA_Update(&sha1_context_, bytes, size); | |
120 } | |
121 | |
122 void MachoID::Update(MachoWalker *walker, unsigned long offset, size_t size) { | |
123 if (!update_function_ || !size) | |
124 return; | |
125 | |
126 // Read up to 4k bytes at a time | |
127 unsigned char buffer[4096]; | |
128 size_t buffer_size; | |
129 off_t file_offset = offset; | |
130 while (size > 0) { | |
131 if (size > sizeof(buffer)) { | |
132 buffer_size = sizeof(buffer); | |
133 size -= buffer_size; | |
134 } else { | |
135 buffer_size = size; | |
136 size = 0; | |
137 } | |
138 | |
139 if (!walker->ReadBytes(buffer, buffer_size, file_offset)) | |
140 return; | |
141 | |
142 (this->*update_function_)(buffer, buffer_size); | |
143 file_offset += buffer_size; | |
144 } | |
145 } | |
146 | |
147 bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) { | |
148 struct breakpad_uuid_command uuid_cmd; | |
149 MachoWalker walker(path_, UUIDWalkerCB, &uuid_cmd); | |
150 | |
151 uuid_cmd.cmd = 0; | |
152 if (!walker.WalkHeader(cpu_type)) | |
153 return false; | |
154 | |
155 // If we found the command, we'll have initialized the uuid_command | |
156 // structure | |
157 if (uuid_cmd.cmd == LC_UUID) { | |
158 memcpy(bytes, uuid_cmd.uuid, sizeof(uuid_cmd.uuid)); | |
159 return true; | |
160 } | |
161 | |
162 return false; | |
163 } | |
164 | |
165 bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) { | |
166 struct dylib_command dylib_cmd; | |
167 MachoWalker walker(path_, IDWalkerCB, &dylib_cmd); | |
168 | |
169 dylib_cmd.cmd = 0; | |
170 if (!walker.WalkHeader(cpu_type)) | |
171 return false; | |
172 | |
173 // If we found the command, we'll have initialized the dylib_command | |
174 // structure | |
175 if (dylib_cmd.cmd == LC_ID_DYLIB) { | |
176 // Take the hashed filename, version, and compatability version bytes | |
177 // to form the first 12 bytes, pad the rest with zeros | |
178 | |
179 // create a crude hash of the filename to generate the first 4 bytes | |
180 identifier[0] = 0; | |
181 identifier[1] = 0; | |
182 identifier[2] = 0; | |
183 identifier[3] = 0; | |
184 | |
185 for (int j = 0, i = strlen(path_)-1; i >= 0 && path_[i]!='/'; ++j, --i) { | |
186 identifier[j%4] += path_[i]; | |
187 } | |
188 | |
189 identifier[4] = (dylib_cmd.dylib.current_version >> 24) & 0xFF; | |
190 identifier[5] = (dylib_cmd.dylib.current_version >> 16) & 0xFF; | |
191 identifier[6] = (dylib_cmd.dylib.current_version >> 8) & 0xFF; | |
192 identifier[7] = dylib_cmd.dylib.current_version & 0xFF; | |
193 identifier[8] = (dylib_cmd.dylib.compatibility_version >> 24) & 0xFF; | |
194 identifier[9] = (dylib_cmd.dylib.compatibility_version >> 16) & 0xFF; | |
195 identifier[10] = (dylib_cmd.dylib.compatibility_version >> 8) & 0xFF; | |
196 identifier[11] = dylib_cmd.dylib.compatibility_version & 0xFF; | |
197 identifier[12] = (cpu_type >> 24) & 0xFF; | |
198 identifier[13] = (cpu_type >> 16) & 0xFF; | |
199 identifier[14] = (cpu_type >> 8) & 0xFF; | |
200 identifier[15] = cpu_type & 0xFF; | |
201 | |
202 return true; | |
203 } | |
204 | |
205 return false; | |
206 } | |
207 | |
208 uint32_t MachoID::Adler32(int cpu_type) { | |
209 MachoWalker walker(path_, WalkerCB, this); | |
210 update_function_ = &MachoID::UpdateCRC; | |
211 crc_ = 0; | |
212 | |
213 if (!walker.WalkHeader(cpu_type)) | |
214 return 0; | |
215 | |
216 return crc_; | |
217 } | |
218 | |
219 bool MachoID::MD5(int cpu_type, unsigned char identifier[16]) { | |
220 MachoWalker walker(path_, WalkerCB, this); | |
221 update_function_ = &MachoID::UpdateMD5; | |
222 | |
223 if (MD5_Init(&md5_context_)) { | |
224 if (!walker.WalkHeader(cpu_type)) | |
225 return false; | |
226 | |
227 MD5_Final(identifier, &md5_context_); | |
228 return true; | |
229 } | |
230 | |
231 return false; | |
232 } | |
233 | |
234 bool MachoID::SHA1(int cpu_type, unsigned char identifier[16]) { | |
235 MachoWalker walker(path_, WalkerCB, this); | |
236 update_function_ = &MachoID::UpdateSHA1; | |
237 | |
238 if (SHA_Init(&sha1_context_)) { | |
239 if (!walker.WalkHeader(cpu_type)) | |
240 return false; | |
241 | |
242 SHA_Final(identifier, &sha1_context_); | |
243 return true; | |
244 } | |
245 | |
246 return false; | |
247 } | |
248 | |
249 // static | |
250 bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, | |
251 bool swap, void *context) { | |
252 MachoID *macho_id = (MachoID *)context; | |
253 | |
254 if (cmd->cmd == LC_SEGMENT) { | |
255 struct segment_command seg; | |
256 | |
257 if (!walker->ReadBytes(&seg, sizeof(seg), offset)) | |
258 return false; | |
259 | |
260 if (swap) | |
261 swap_segment_command(&seg, NXHostByteOrder()); | |
262 | |
263 struct mach_header_64 header; | |
264 off_t header_offset; | |
265 | |
266 if (!walker->CurrentHeader(&header, &header_offset)) | |
267 return false; | |
268 | |
269 // Process segments that have sections: | |
270 // (e.g., __TEXT, __DATA, __IMPORT, __OBJC) | |
271 offset += sizeof(struct segment_command); | |
272 struct section sec; | |
273 for (unsigned long i = 0; i < seg.nsects; ++i) { | |
274 if (!walker->ReadBytes(&sec, sizeof(sec), offset)) | |
275 return false; | |
276 | |
277 if (swap) | |
278 swap_section(&sec, 1, NXHostByteOrder()); | |
279 | |
280 // sections of type S_ZEROFILL are "virtual" and contain no data | |
281 // in the file itself | |
282 if ((sec.flags & SECTION_TYPE) != S_ZEROFILL && sec.offset != 0) | |
283 macho_id->Update(walker, header_offset + sec.offset, sec.size); | |
284 | |
285 offset += sizeof(struct section); | |
286 } | |
287 } else if (cmd->cmd == LC_SEGMENT_64) { | |
288 struct segment_command_64 seg64; | |
289 | |
290 if (!walker->ReadBytes(&seg64, sizeof(seg64), offset)) | |
291 return false; | |
292 | |
293 if (swap) | |
294 breakpad_swap_segment_command_64(&seg64, NXHostByteOrder()); | |
295 | |
296 struct mach_header_64 header; | |
297 off_t header_offset; | |
298 | |
299 if (!walker->CurrentHeader(&header, &header_offset)) | |
300 return false; | |
301 | |
302 // Process segments that have sections: | |
303 // (e.g., __TEXT, __DATA, __IMPORT, __OBJC) | |
304 offset += sizeof(struct segment_command_64); | |
305 struct section_64 sec64; | |
306 for (unsigned long i = 0; i < seg64.nsects; ++i) { | |
307 if (!walker->ReadBytes(&sec64, sizeof(sec64), offset)) | |
308 return false; | |
309 | |
310 if (swap) | |
311 breakpad_swap_section_64(&sec64, 1, NXHostByteOrder()); | |
312 | |
313 // sections of type S_ZEROFILL are "virtual" and contain no data | |
314 // in the file itself | |
315 if ((sec64.flags & SECTION_TYPE) != S_ZEROFILL && sec64.offset != 0) | |
316 macho_id->Update(walker, header_offset + sec64.offset, sec64.size); | |
317 | |
318 offset += sizeof(struct section_64); | |
319 } | |
320 } | |
321 | |
322 // Continue processing | |
323 return true; | |
324 } | |
325 | |
326 // static | |
327 bool MachoID::UUIDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, | |
328 bool swap, void *context) { | |
329 if (cmd->cmd == LC_UUID) { | |
330 struct breakpad_uuid_command *uuid_cmd = | |
331 (struct breakpad_uuid_command *)context; | |
332 | |
333 if (!walker->ReadBytes(uuid_cmd, sizeof(struct breakpad_uuid_command), | |
334 offset)) | |
335 return false; | |
336 | |
337 if (swap) | |
338 breakpad_swap_uuid_command(uuid_cmd, NXHostByteOrder()); | |
339 | |
340 return false; | |
341 } | |
342 | |
343 // Continue processing | |
344 return true; | |
345 } | |
346 | |
347 // static | |
348 bool MachoID::IDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, | |
349 bool swap, void *context) { | |
350 if (cmd->cmd == LC_ID_DYLIB) { | |
351 struct dylib_command *dylib_cmd = (struct dylib_command *)context; | |
352 | |
353 if (!walker->ReadBytes(dylib_cmd, sizeof(struct dylib_command), offset)) | |
354 return false; | |
355 | |
356 if (swap) | |
357 swap_dylib_command(dylib_cmd, NXHostByteOrder()); | |
358 | |
359 return false; | |
360 } | |
361 | |
362 // Continue processing | |
363 return true; | |
364 } | |
365 | |
366 } // namespace MacFileUtilities | |
OLD | NEW |