macho_id.cc
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 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 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 67 MachoID::~MachoID() {} 68 69 void MachoID::UpdateMD5(unsigned char* bytes, size_t size) { 70 MD5Update(&md5_context_, bytes, static_cast<unsigned>(size)); 71 } 72 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 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 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 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 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 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