1 /*
2 Copyright (c) 2007-2015. The YARA Authors. All Rights Reserved.
3 
4 Redistribution and use in source and binary forms, with or without modification,
5 are permitted provided that the following conditions are met:
6 
7 1. Redistributions of source code must retain the above copyright notice, this
8 list of conditions and the following disclaimer.
9 
10 2. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation and/or
12 other materials provided with the distribution.
13 
14 3. Neither the name of the copyright holder nor the names of its contributors
15 may be used to endorse or promote products derived from this software without
16 specific prior written permission.
17 
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 
30 #include <fcntl.h>
31 
32 #if defined(_WIN32) || defined(__CYGWIN__)
33 #include <windows.h>
34 #else
35 #include <sys/mman.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #endif
39 
40 #include <yara/error.h>
41 #include <yara/filemap.h>
42 
43 ////////////////////////////////////////////////////////////////////////////////
44 // Maps a whole file into memory.
45 //
46 // Args:
47 //   file_path: Path of the file to map.
48 //   pmapped_file: Pointer to a YR_MAPPED_FILE that will be filled with
49 //                 information about the mapping.
50 // Returns:
51 //   ERROR_SUCCESS
52 //   ERROR_INVALID_ARGUMENT
53 //   ERROR_COULD_NOT_OPEN_FILE
54 //   ERROR_COULD_NOT_MAP_FILE
55 //
yr_filemap_map(const char * file_path,YR_MAPPED_FILE * pmapped_file)56 YR_API int yr_filemap_map(const char* file_path, YR_MAPPED_FILE* pmapped_file)
57 {
58   return yr_filemap_map_ex(file_path, 0, 0, pmapped_file);
59 }
60 
61 ////////////////////////////////////////////////////////////////////////////////
62 // Maps a portion of a file (specified by descriptor) into memory.
63 //
64 // Args:
65 //   file: File descriptor representing the file to map.
66 //   offset: File offset where the mapping will begin. This offset must be
67 //           multiple of 1MB and not greater than the actual file size.
68 //   size: Number of bytes that will be mapped. If zero or greater than the
69 //         actual file size all content until the end of the file will be
70 //         mapped.
71 //   pmapped_file: Pointer to a YR_MAPPED_FILE struct that will be filled with
72 //                 the new mapping.
73 // Returns:
74 //   ERROR_SUCCESS
75 //   ERROR_INVALID_ARGUMENT
76 //   ERROR_COULD_NOT_OPEN_FILE
77 //   ERROR_COULD_NOT_MAP_FILE
78 //
79 #if defined(_WIN32) || defined(__CYGWIN__)
80 
yr_filemap_map_fd(YR_FILE_DESCRIPTOR file,uint64_t offset,size_t size,YR_MAPPED_FILE * pmapped_file)81 YR_API int yr_filemap_map_fd(
82     YR_FILE_DESCRIPTOR file,
83     uint64_t offset,
84     size_t size,
85     YR_MAPPED_FILE* pmapped_file)
86 {
87   LARGE_INTEGER fs;
88   size_t file_size;
89 
90   pmapped_file->file = file;
91   pmapped_file->mapping = NULL;
92   pmapped_file->data = NULL;
93   pmapped_file->size = 0;
94 
95   // Ensure that offset is aligned to 1MB
96   if (offset >> 20 << 20 != offset)
97     return ERROR_INVALID_ARGUMENT;
98 
99   if (GetFileSizeEx(pmapped_file->file, &fs))
100   {
101 #ifdef _WIN64
102     file_size = fs.QuadPart;
103 #else
104     file_size = fs.LowPart;
105 #endif
106   }
107   else
108   {
109     pmapped_file->file = INVALID_HANDLE_VALUE;
110     return ERROR_COULD_NOT_OPEN_FILE;
111   }
112 
113   if (offset > (uint64_t) file_size)
114     return ERROR_COULD_NOT_MAP_FILE;
115 
116   if (size == 0)
117     size = (size_t)(file_size - offset);
118 
119   pmapped_file->size = yr_min(size, (size_t)(file_size - offset));
120 
121   if (pmapped_file->size != 0)
122   {
123     pmapped_file->mapping = CreateFileMapping(
124         pmapped_file->file, NULL, PAGE_READONLY, 0, 0, NULL);
125 
126     if (pmapped_file->mapping == NULL)
127     {
128       pmapped_file->file = INVALID_HANDLE_VALUE;
129       pmapped_file->size = 0;
130       return ERROR_COULD_NOT_MAP_FILE;
131     }
132 
133     pmapped_file->data = (const uint8_t*) MapViewOfFile(
134         pmapped_file->mapping,
135         FILE_MAP_READ,
136         offset >> 32,
137         offset & 0xFFFFFFFF,
138         pmapped_file->size);
139 
140     if (pmapped_file->data == NULL)
141     {
142       CloseHandle(pmapped_file->mapping);
143       pmapped_file->mapping = NULL;
144       pmapped_file->file = INVALID_HANDLE_VALUE;
145       pmapped_file->size = 0;
146       return ERROR_COULD_NOT_MAP_FILE;
147     }
148   }
149   else
150   {
151     pmapped_file->mapping = NULL;
152     pmapped_file->data = NULL;
153   }
154 
155   return ERROR_SUCCESS;
156 }
157 
158 #else  // POSIX
159 
160 #define MAP_EXTRA_FLAGS 0
161 
162 #if defined(__APPLE__)
163 // MacOS defines some extra flags for mmap.The MAP_RESILIENT_CODESIGN allows
164 // to read from binaries whose code signature is invalid, without this flags
165 // any attempt to read from such binaries causes a crash, see:
166 // https://github.com/VirusTotal/yara/issues/1309.
167 //
168 // Also, reading from files in removable media that becomes unavailable
169 // crashes the program if the MAP_RESILIENT_MEDIA flag is not set.
170 #if defined(MAP_RESILIENT_CODESIGN)
171 #undef MAP_EXTRA_FLAGS
172 #if defined(MAP_RESILIENT_MEDIA)
173 #define MAP_EXTRA_FLAGS MAP_RESILIENT_CODESIGN | MAP_RESILIENT_MEDIA
174 #else
175 #define MAP_EXTRA_FLAGS MAP_RESILIENT_CODESIGN
176 #endif
177 #endif  // #if defined(MAP_RESILIENT_CODESIGN)
178 #endif  // #if defined (__APPLE__)
179 
yr_filemap_map_fd(YR_FILE_DESCRIPTOR file,uint64_t offset,size_t size,YR_MAPPED_FILE * pmapped_file)180 YR_API int yr_filemap_map_fd(
181     YR_FILE_DESCRIPTOR file,
182     uint64_t offset,
183     size_t size,
184     YR_MAPPED_FILE* pmapped_file)
185 {
186   struct stat st;
187 
188   pmapped_file->file = file;
189   pmapped_file->data = NULL;
190   pmapped_file->size = 0;
191 
192   // Ensure that offset is aligned to 1MB
193   if (offset >> 20 << 20 != offset)
194     return ERROR_INVALID_ARGUMENT;
195 
196   if (fstat(file, &st) != 0 || S_ISDIR(st.st_mode))
197     return ERROR_COULD_NOT_OPEN_FILE;
198 
199   if (offset > st.st_size)
200     return ERROR_COULD_NOT_MAP_FILE;
201 
202   if (size == 0)
203     size = (size_t)(st.st_size - offset);
204 
205   pmapped_file->size = yr_min(size, (size_t)(st.st_size - offset));
206 
207   if (pmapped_file->size != 0)
208   {
209     pmapped_file->data = (const uint8_t*) mmap(
210         0,
211         pmapped_file->size,
212         PROT_READ,
213         MAP_PRIVATE | MAP_EXTRA_FLAGS,
214         pmapped_file->file,
215         offset);
216 
217     if (pmapped_file->data == MAP_FAILED)
218     {
219       pmapped_file->data = NULL;
220       pmapped_file->size = 0;
221       pmapped_file->file = -1;
222 
223       return ERROR_COULD_NOT_MAP_FILE;
224     }
225 
226     madvise((void*) pmapped_file->data, pmapped_file->size, MADV_SEQUENTIAL);
227   }
228   else
229   {
230     pmapped_file->data = NULL;
231   }
232 
233   return ERROR_SUCCESS;
234 }
235 
236 #endif
237 
238 ////////////////////////////////////////////////////////////////////////////////
239 // Maps a portion of a file (specified by path) into memory.
240 //
241 // Args:
242 //   file_path: Path of the file to map.
243 //   offset: File offset where the mapping will begin. This offset must be
244 //           multiple of 1MB and not greater than the actual file size.
245 //   size: Number of bytes that will be mapped. If zero or greater than the
246 //         actual file size all content until the end of the file will be
247 //         mapped.
248 //   pmapped_file: Pointer to a YR_MAPPED_FILE struct that will be filled with
249 //                 the new mapping.
250 // Returns:
251 //   ERROR_SUCCESS
252 //   ERROR_INVALID_ARGUMENT
253 //   ERROR_COULD_NOT_OPEN_FILE
254 //   ERROR_COULD_NOT_MAP_FILE
255 //
256 #if defined(_WIN32) || defined(__CYGWIN__)
257 
yr_filemap_map_ex(const char * file_path,uint64_t offset,size_t size,YR_MAPPED_FILE * pmapped_file)258 YR_API int yr_filemap_map_ex(
259     const char* file_path,
260     uint64_t offset,
261     size_t size,
262     YR_MAPPED_FILE* pmapped_file)
263 {
264   YR_FILE_DESCRIPTOR fd;
265   int result;
266 
267   if (file_path == NULL)
268     return ERROR_INVALID_ARGUMENT;
269 
270   fd = CreateFileA(
271       file_path,
272       GENERIC_READ,
273       FILE_SHARE_READ | FILE_SHARE_WRITE,
274       NULL,
275       OPEN_EXISTING,
276       FILE_FLAG_SEQUENTIAL_SCAN,
277       NULL);
278 
279   if (fd == INVALID_HANDLE_VALUE)
280     return ERROR_COULD_NOT_OPEN_FILE;
281 
282   result = yr_filemap_map_fd(fd, offset, size, pmapped_file);
283 
284   if (result != ERROR_SUCCESS)
285     CloseHandle(fd);
286 
287   return result;
288 }
289 
290 #else  // POSIX
291 
yr_filemap_map_ex(const char * file_path,uint64_t offset,size_t size,YR_MAPPED_FILE * pmapped_file)292 YR_API int yr_filemap_map_ex(
293     const char* file_path,
294     uint64_t offset,
295     size_t size,
296     YR_MAPPED_FILE* pmapped_file)
297 {
298   YR_FILE_DESCRIPTOR fd;
299   int result;
300 
301   if (file_path == NULL)
302     return ERROR_INVALID_ARGUMENT;
303 
304   fd = open(file_path, O_RDONLY);
305 
306   if (fd == -1)
307     return ERROR_COULD_NOT_OPEN_FILE;
308 
309   result = yr_filemap_map_fd(fd, offset, size, pmapped_file);
310 
311   if (result != ERROR_SUCCESS)
312     close(fd);
313 
314   return result;
315 }
316 
317 #endif
318 
319 ////////////////////////////////////////////////////////////////////////////////
320 // Unmaps a file mapping.
321 //
322 // Args:
323 //   pmapped_file: Pointer to a YR_MAPPED_FILE that struct.
324 //
325 #ifdef WIN32
326 
yr_filemap_unmap_fd(YR_MAPPED_FILE * pmapped_file)327 YR_API void yr_filemap_unmap_fd(YR_MAPPED_FILE* pmapped_file)
328 {
329   if (pmapped_file->data != NULL)
330     UnmapViewOfFile(pmapped_file->data);
331 
332   if (pmapped_file->mapping != NULL)
333     CloseHandle(pmapped_file->mapping);
334 
335   pmapped_file->mapping = NULL;
336   pmapped_file->data = NULL;
337   pmapped_file->size = 0;
338 }
339 
yr_filemap_unmap(YR_MAPPED_FILE * pmapped_file)340 YR_API void yr_filemap_unmap(YR_MAPPED_FILE* pmapped_file)
341 {
342   yr_filemap_unmap_fd(pmapped_file);
343 
344   if (pmapped_file->file != INVALID_HANDLE_VALUE)
345   {
346     CloseHandle(pmapped_file->file);
347     pmapped_file->file = INVALID_HANDLE_VALUE;
348   }
349 }
350 
351 #else  // POSIX
352 
yr_filemap_unmap_fd(YR_MAPPED_FILE * pmapped_file)353 YR_API void yr_filemap_unmap_fd(YR_MAPPED_FILE* pmapped_file)
354 {
355   if (pmapped_file->data != NULL)
356     munmap((void*) pmapped_file->data, pmapped_file->size);
357 
358   pmapped_file->data = NULL;
359   pmapped_file->size = 0;
360 }
361 
yr_filemap_unmap(YR_MAPPED_FILE * pmapped_file)362 YR_API void yr_filemap_unmap(YR_MAPPED_FILE* pmapped_file)
363 {
364   yr_filemap_unmap_fd(pmapped_file);
365 
366   if (pmapped_file->file != -1)
367   {
368     close(pmapped_file->file);
369     pmapped_file->file = -1;
370   }
371 }
372 
373 #endif
374