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() {}
78 MachOImageReader::MachOImageReader()
85 MachOImageReader::~MachOImageReader() {}
87 bool MachOImageReader::Initialize(const uint8_t* image
, size_t image_size
) {
91 data_
.reset(new ByteSlice(image
, image_size
));
93 const uint32_t* magic
= data_
->GetPointerAt
<uint32_t>(0);
97 // Check if this is a fat file. Note that the fat_header and fat_arch
98 // structs are always in big endian.
99 is_fat_
= *magic
== FAT_MAGIC
|| *magic
== FAT_CIGAM
;
101 const fat_header
* header
= data_
->GetPointerAt
<fat_header
>(0);
105 bool do_swap
= header
->magic
== FAT_CIGAM
;
106 uint32_t nfat_arch
= do_swap
? OSSwapInt32(header
->nfat_arch
)
109 size_t offset
= sizeof(*header
);
110 for (uint32_t i
= 0; i
< nfat_arch
; ++i
) {
111 const fat_arch
* arch
= data_
->GetPointerAt
<fat_arch
>(offset
);
115 uint32_t arch_offset
= do_swap
? OSSwapInt32(arch
->offset
) : arch
->offset
;
116 uint32_t arch_size
= do_swap
? OSSwapInt32(arch
->size
) : arch
->size
;
118 ByteSlice slice
= data_
->Slice(arch_offset
, arch_size
);
119 if (!slice
.IsValid())
122 fat_images_
.push_back(new MachOImageReader());
123 if (!fat_images_
.back()->Initialize(slice
.data(), slice
.size()))
126 offset
+= sizeof(*arch
);
132 bool do_swap
= *magic
== MH_CIGAM
|| *magic
== MH_CIGAM_64
;
134 // Make sure this is a Mach-O file.
135 is_64_bit_
= *magic
== MH_MAGIC_64
|| *magic
== MH_CIGAM_64
;
136 if (!(is_64_bit_
|| *magic
== MH_MAGIC
|| do_swap
))
139 // Read the full Mach-O image header.
141 if (!GetMachHeader64())
144 if (!GetMachHeader())
148 // Collect all the load commands for the binary.
149 const size_t load_command_size
= sizeof(load_command
);
150 size_t offset
= is_64_bit_
? sizeof(mach_header_64
) : sizeof(mach_header
);
151 const uint32_t num_commands
= do_swap
? OSSwapInt32(GetMachHeader()->ncmds
)
152 : GetMachHeader()->ncmds
;
153 commands_
.resize(num_commands
);
154 for (uint32_t i
= 0; i
< num_commands
; ++i
) {
155 LoadCommand
* command
= &commands_
[i
];
157 command
->data
.resize(load_command_size
);
158 if (!data_
->CopyDataAt(offset
, load_command_size
, &command
->data
[0])) {
162 uint32_t cmdsize
= do_swap
? OSSwapInt32(command
->cmdsize())
163 : command
->cmdsize();
164 command
->data
.resize(cmdsize
);
165 if (!data_
->CopyDataAt(offset
, cmdsize
, &command
->data
[0])) {
175 bool MachOImageReader::IsFat() {
179 std::vector
<MachOImageReader
*> MachOImageReader::GetFatImages() {
181 std::vector
<MachOImageReader
*> images
;
182 for (auto it
= fat_images_
.begin(); it
!= fat_images_
.end(); ++it
)
183 images
.push_back(*it
);
187 bool MachOImageReader::Is64Bit() {
192 const mach_header
* MachOImageReader::GetMachHeader() {
194 return data_
->GetPointerAt
<mach_header
>(0);
197 const mach_header_64
* MachOImageReader::GetMachHeader64() {
200 return data_
->GetPointerAt
<mach_header_64
>(0);
203 uint32_t MachOImageReader::GetFileType() {
205 return GetMachHeader()->filetype
;
208 const std::vector
<MachOImageReader::LoadCommand
>&
209 MachOImageReader::GetLoadCommands() {
214 bool MachOImageReader::GetCodeSignatureInfo(std::vector
<uint8_t>* info
) {
216 DCHECK(info
->empty());
218 // Find the LC_CODE_SIGNATURE command and cast it to its linkedit format.
219 const linkedit_data_command
* lc_code_signature
= nullptr;
220 for (const auto& command
: commands_
) {
221 if (command
.cmd() == LC_CODE_SIGNATURE
) {
222 lc_code_signature
= command
.as_command
<linkedit_data_command
>();
226 if (lc_code_signature
== nullptr)
229 info
->resize(lc_code_signature
->datasize
);
230 return data_
->CopyDataAt(lc_code_signature
->dataoff
,
231 lc_code_signature
->datasize
,
235 } // namespace safe_browsing