1 // Copyright 2006 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 // * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 // macho_id.cc: Functions to gather identifying information from a macho file
30 //
31 // See macho_id.h for documentation
32 //
33 // Author: Dan Waylonis
34
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h> // Must come first
38 #endif
39
40 #include <fcntl.h>
41 #include <mach-o/loader.h>
42 #include <stdio.h>
43 #include <string.h>
44
45 #include "common/mac/macho_id.h"
46 #include "common/mac/macho_walker.h"
47 #include "common/mac/macho_utilities.h"
48
49 namespace MacFileUtilities {
50
51 using google_breakpad::MD5Init;
52 using google_breakpad::MD5Update;
53 using google_breakpad::MD5Final;
54
MachoID(const char * path)55 MachoID::MachoID(const char* path)
56 : memory_(0), memory_size_(0), md5_context_(), update_function_(NULL) {
57 snprintf(path_, sizeof(path_), "%s", path);
58 }
59
MachoID(void * memory,size_t size)60 MachoID::MachoID(void* memory, size_t size)
61 : path_(),
62 memory_(memory),
63 memory_size_(size),
64 md5_context_(),
65 update_function_(NULL) {}
66
~MachoID()67 MachoID::~MachoID() {}
68
UpdateMD5(unsigned char * bytes,size_t size)69 void MachoID::UpdateMD5(unsigned char* bytes, size_t size) {
70 MD5Update(&md5_context_, bytes, static_cast<unsigned>(size));
71 }
72
Update(MachoWalker * walker,off_t offset,size_t size)73 void MachoID::Update(MachoWalker* walker, off_t offset, size_t size) {
74 if (!update_function_ || !size)
75 return;
76
77 // Read up to 4k bytes at a time
78 unsigned char buffer[4096];
79 size_t buffer_size;
80 off_t file_offset = offset;
81 while (size > 0) {
82 if (size > sizeof(buffer)) {
83 buffer_size = sizeof(buffer);
84 size -= buffer_size;
85 } else {
86 buffer_size = size;
87 size = 0;
88 }
89
90 if (!walker->ReadBytes(buffer, buffer_size, file_offset))
91 return;
92
93 (this->*update_function_)(buffer, buffer_size);
94 file_offset += buffer_size;
95 }
96 }
97
UUIDCommand(cpu_type_t cpu_type,cpu_subtype_t cpu_subtype,unsigned char bytes[16])98 bool MachoID::UUIDCommand(cpu_type_t cpu_type,
99 cpu_subtype_t cpu_subtype,
100 unsigned char bytes[16]) {
101 struct breakpad_uuid_command uuid_cmd;
102 uuid_cmd.cmd = 0;
103 if (!WalkHeader(cpu_type, cpu_subtype, UUIDWalkerCB, &uuid_cmd))
104 return false;
105
106 // If we found the command, we'll have initialized the uuid_command
107 // structure
108 if (uuid_cmd.cmd == LC_UUID) {
109 memcpy(bytes, uuid_cmd.uuid, sizeof(uuid_cmd.uuid));
110 return true;
111 }
112
113 return false;
114 }
115
MD5(cpu_type_t cpu_type,cpu_subtype_t cpu_subtype,unsigned char identifier[16])116 bool MachoID::MD5(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype, unsigned char identifier[16]) {
117 update_function_ = &MachoID::UpdateMD5;
118
119 MD5Init(&md5_context_);
120
121 if (!WalkHeader(cpu_type, cpu_subtype, WalkerCB, this))
122 return false;
123
124 MD5Final(identifier, &md5_context_);
125 return true;
126 }
127
WalkHeader(cpu_type_t cpu_type,cpu_subtype_t cpu_subtype,MachoWalker::LoadCommandCallback callback,void * context)128 bool MachoID::WalkHeader(cpu_type_t cpu_type,
129 cpu_subtype_t cpu_subtype,
130 MachoWalker::LoadCommandCallback callback,
131 void* context) {
132 if (memory_) {
133 MachoWalker walker(memory_, memory_size_, callback, context);
134 return walker.WalkHeader(cpu_type, cpu_subtype);
135 } else {
136 MachoWalker walker(path_, callback, context);
137 return walker.WalkHeader(cpu_type, cpu_subtype);
138 }
139 }
140
141 // static
WalkerCB(MachoWalker * walker,load_command * cmd,off_t offset,bool swap,void * context)142 bool MachoID::WalkerCB(MachoWalker* walker, load_command* cmd, off_t offset,
143 bool swap, void* context) {
144 MachoID* macho_id = (MachoID*)context;
145
146 if (cmd->cmd == LC_SEGMENT) {
147 struct segment_command seg;
148
149 if (!walker->ReadBytes(&seg, sizeof(seg), offset))
150 return false;
151
152 if (swap)
153 breakpad_swap_segment_command(&seg);
154
155 struct mach_header_64 header;
156 off_t header_offset;
157
158 if (!walker->CurrentHeader(&header, &header_offset))
159 return false;
160
161 // Process segments that have sections:
162 // (e.g., __TEXT, __DATA, __IMPORT, __OBJC)
163 offset += sizeof(struct segment_command);
164 struct section sec;
165 for (unsigned long i = 0; i < seg.nsects; ++i) {
166 if (!walker->ReadBytes(&sec, sizeof(sec), offset))
167 return false;
168
169 if (swap)
170 breakpad_swap_section(&sec, 1);
171
172 // sections of type S_ZEROFILL are "virtual" and contain no data
173 // in the file itself
174 if ((sec.flags & SECTION_TYPE) != S_ZEROFILL && sec.offset != 0)
175 macho_id->Update(walker, header_offset + sec.offset, sec.size);
176
177 offset += sizeof(struct section);
178 }
179 } else if (cmd->cmd == LC_SEGMENT_64) {
180 struct segment_command_64 seg64;
181
182 if (!walker->ReadBytes(&seg64, sizeof(seg64), offset))
183 return false;
184
185 if (swap)
186 breakpad_swap_segment_command_64(&seg64);
187
188 struct mach_header_64 header;
189 off_t header_offset;
190
191 if (!walker->CurrentHeader(&header, &header_offset))
192 return false;
193
194 // Process segments that have sections:
195 // (e.g., __TEXT, __DATA, __IMPORT, __OBJC)
196 offset += sizeof(struct segment_command_64);
197 struct section_64 sec64;
198 for (unsigned long i = 0; i < seg64.nsects; ++i) {
199 if (!walker->ReadBytes(&sec64, sizeof(sec64), offset))
200 return false;
201
202 if (swap)
203 breakpad_swap_section_64(&sec64, 1);
204
205 // sections of type S_ZEROFILL are "virtual" and contain no data
206 // in the file itself
207 if ((sec64.flags & SECTION_TYPE) != S_ZEROFILL && sec64.offset != 0)
208 macho_id->Update(walker,
209 header_offset + sec64.offset,
210 (size_t)sec64.size);
211
212 offset += sizeof(struct section_64);
213 }
214 }
215
216 // Continue processing
217 return true;
218 }
219
220 // static
UUIDWalkerCB(MachoWalker * walker,load_command * cmd,off_t offset,bool swap,void * context)221 bool MachoID::UUIDWalkerCB(MachoWalker* walker, load_command* cmd, off_t offset,
222 bool swap, void* context) {
223 if (cmd->cmd == LC_UUID) {
224 struct breakpad_uuid_command* uuid_cmd =
225 (struct breakpad_uuid_command*)context;
226
227 if (!walker->ReadBytes(uuid_cmd, sizeof(struct breakpad_uuid_command),
228 offset))
229 return false;
230
231 if (swap)
232 breakpad_swap_uuid_command(uuid_cmd);
233
234 return false;
235 }
236
237 // Continue processing
238 return true;
239 }
240 } // namespace MacFileUtilities
241