1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/common/safe_browsing/mach_o_image_reader_mac.h"
7 #include <libkern/OSByteOrder.h>
8 #include <mach-o/fat.h>
9 #include <mach-o/loader.h>
11 #include "base/logging.h"
12 #include "base/numerics/safe_math.h"
14 namespace safe_browsing
{
16 // ByteSlice is a bounds-checking view of an arbitrary byte array.
19 // Creates an invalid byte slice.
20 ByteSlice() : ByteSlice(nullptr, 0) {}
22 // Creates a slice for a given data array.
23 explicit ByteSlice(const uint8_t* data
, size_t size
)
24 : data_(data
), size_(size
) {}
28 return data_
!= nullptr;
31 // Creates a sub-slice from the current slice.
32 ByteSlice
Slice(size_t at
, size_t size
) {
33 if (!RangeCheck(at
, size
))
35 return ByteSlice(data_
+ at
, size
);
38 // Casts an offset to a specific type.
40 const T
* GetPointerAt(size_t at
) {
41 if (!RangeCheck(at
, sizeof(T
)))
43 return reinterpret_cast<const T
*>(data_
+ at
);
46 // Copies data from an offset to a buffer.
47 bool CopyDataAt(size_t at
, size_t size
, uint8_t* out_data
) {
48 if (!RangeCheck(at
, size
))
50 memcpy(out_data
, data_
+ at
, size
);
54 bool RangeCheck(size_t offset
, size_t size
) {
57 base::CheckedNumeric
<size_t> range(offset
);
61 return range
.ValueOrDie() <= size_
;
64 const uint8_t* data() const { return data_
; }
65 size_t size() const { return size_
; }
71 // Copy and assign allowed.
74 MachOImageReader::LoadCommand::LoadCommand() {}
76 MachOImageReader::LoadCommand::~LoadCommand() {}
79 bool MachOImageReader::IsMachOMagicValue(uint32_t magic
) {
80 return magic
== FAT_MAGIC
|| magic
== FAT_CIGAM
||
81 magic
== MH_MAGIC
|| magic
== MH_CIGAM
||
82 magic
== MH_MAGIC_64
|| magic
== MH_CIGAM_64
;
85 MachOImageReader::MachOImageReader()
92 MachOImageReader::~MachOImageReader() {}
94 bool MachOImageReader::Initialize(const uint8_t* image
, size_t image_size
) {
98 data_
.reset(new ByteSlice(image
, image_size
));
100 const uint32_t* magic
= data_
->GetPointerAt
<uint32_t>(0);
104 // Check if this is a fat file. Note that the fat_header and fat_arch
105 // structs are always in big endian.
106 is_fat_
= *magic
== FAT_MAGIC
|| *magic
== FAT_CIGAM
;
108 const fat_header
* header
= data_
->GetPointerAt
<fat_header
>(0);
112 bool do_swap
= header
->magic
== FAT_CIGAM
;
113 uint32_t nfat_arch
= do_swap
? OSSwapInt32(header
->nfat_arch
)
116 size_t offset
= sizeof(*header
);
117 for (uint32_t i
= 0; i
< nfat_arch
; ++i
) {
118 const fat_arch
* arch
= data_
->GetPointerAt
<fat_arch
>(offset
);
122 uint32_t arch_offset
= do_swap
? OSSwapInt32(arch
->offset
) : arch
->offset
;
123 uint32_t arch_size
= do_swap
? OSSwapInt32(arch
->size
) : arch
->size
;
125 ByteSlice slice
= data_
->Slice(arch_offset
, arch_size
);
126 if (!slice
.IsValid())
129 fat_images_
.push_back(new MachOImageReader());
130 if (!fat_images_
.back()->Initialize(slice
.data(), slice
.size()))
133 offset
+= sizeof(*arch
);
139 bool do_swap
= *magic
== MH_CIGAM
|| *magic
== MH_CIGAM_64
;
141 // Make sure this is a Mach-O file.
142 is_64_bit_
= *magic
== MH_MAGIC_64
|| *magic
== MH_CIGAM_64
;
143 if (!(is_64_bit_
|| *magic
== MH_MAGIC
|| do_swap
))
146 // Read the full Mach-O image header.
148 if (!GetMachHeader64())
151 if (!GetMachHeader())
155 // Collect all the load commands for the binary.
156 const size_t load_command_size
= sizeof(load_command
);
157 size_t offset
= is_64_bit_
? sizeof(mach_header_64
) : sizeof(mach_header
);
158 const uint32_t num_commands
= do_swap
? OSSwapInt32(GetMachHeader()->ncmds
)
159 : GetMachHeader()->ncmds
;
160 commands_
.resize(num_commands
);
161 for (uint32_t i
= 0; i
< num_commands
; ++i
) {
162 LoadCommand
* command
= &commands_
[i
];
164 command
->data
.resize(load_command_size
);
165 if (!data_
->CopyDataAt(offset
, load_command_size
, &command
->data
[0])) {
169 uint32_t cmdsize
= do_swap
? OSSwapInt32(command
->cmdsize())
170 : command
->cmdsize();
171 command
->data
.resize(cmdsize
);
172 if (!data_
->CopyDataAt(offset
, cmdsize
, &command
->data
[0])) {
182 bool MachOImageReader::IsFat() {
186 std::vector
<MachOImageReader
*> MachOImageReader::GetFatImages() {
188 std::vector
<MachOImageReader
*> images
;
189 for (auto it
= fat_images_
.begin(); it
!= fat_images_
.end(); ++it
)
190 images
.push_back(*it
);
194 bool MachOImageReader::Is64Bit() {
199 const mach_header
* MachOImageReader::GetMachHeader() {
201 return data_
->GetPointerAt
<mach_header
>(0);
204 const mach_header_64
* MachOImageReader::GetMachHeader64() {
207 return data_
->GetPointerAt
<mach_header_64
>(0);
210 uint32_t MachOImageReader::GetFileType() {
212 return GetMachHeader()->filetype
;
215 const std::vector
<MachOImageReader::LoadCommand
>&
216 MachOImageReader::GetLoadCommands() {
221 bool MachOImageReader::GetCodeSignatureInfo(std::vector
<uint8_t>* info
) {
223 DCHECK(info
->empty());
225 // Find the LC_CODE_SIGNATURE command and cast it to its linkedit format.
226 const linkedit_data_command
* lc_code_signature
= nullptr;
227 for (const auto& command
: commands_
) {
228 if (command
.cmd() == LC_CODE_SIGNATURE
) {
229 lc_code_signature
= command
.as_command
<linkedit_data_command
>();
233 if (lc_code_signature
== nullptr)
236 info
->resize(lc_code_signature
->datasize
);
237 return data_
->CopyDataAt(lc_code_signature
->dataoff
,
238 lc_code_signature
->datasize
,
242 } // namespace safe_browsing