1 /*
2  * MZ header functions
3  *
4  * Copyright (C) 2011-2021, Joachim Metz <joachim.metz@gmail.com>
5  *
6  * Refer to AUTHORS for acknowledgements.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <common.h>
23 #include <byte_stream.h>
24 #include <memory.h>
25 #include <types.h>
26 
27 #include "libexe_libcerror.h"
28 #include "libexe_libcnotify.h"
29 #include "libexe_mz_header.h"
30 
31 #include "exe_mz_header.h"
32 
33 /* Creates a MZ header
34  * Make sure the value mz_header is referencing, is set to NULL
35  * Returns 1 if successful or -1 on error
36  */
libexe_mz_header_initialize(libexe_mz_header_t ** mz_header,libcerror_error_t ** error)37 int libexe_mz_header_initialize(
38      libexe_mz_header_t **mz_header,
39      libcerror_error_t **error )
40 {
41 	static char *function = "libexe_mz_header_initialize";
42 
43 	if( mz_header == NULL )
44 	{
45 		libcerror_error_set(
46 		 error,
47 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
48 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
49 		 "%s: invalid MZ header.",
50 		 function );
51 
52 		return( -1 );
53 	}
54 	if( *mz_header != NULL )
55 	{
56 		libcerror_error_set(
57 		 error,
58 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
59 		 LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
60 		 "%s: invalid MZ header value already set.",
61 		 function );
62 
63 		return( -1 );
64 	}
65 	*mz_header = memory_allocate_structure(
66 	              libexe_mz_header_t );
67 
68 	if( *mz_header == NULL )
69 	{
70 		libcerror_error_set(
71 		 error,
72 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
73 		 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
74 		 "%s: unable to create MZ header.",
75 		 function );
76 
77 		goto on_error;
78 	}
79 	if( memory_set(
80 	     *mz_header,
81 	     0,
82 	     sizeof( libexe_mz_header_t ) ) == NULL )
83 	{
84 		libcerror_error_set(
85 		 error,
86 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
87 		 LIBCERROR_MEMORY_ERROR_SET_FAILED,
88 		 "%s: unable to clear file.",
89 		 function );
90 
91 		goto on_error;
92 	}
93 	return( 1 );
94 
95 on_error:
96 	if( *mz_header != NULL )
97 	{
98 		memory_free(
99 		 *mz_header );
100 
101 		*mz_header = NULL;
102 	}
103 	return( -1 );
104 }
105 
106 /* Frees a MZ header
107  * Returns 1 if successful or -1 on error
108  */
libexe_mz_header_free(libexe_mz_header_t ** mz_header,libcerror_error_t ** error)109 int libexe_mz_header_free(
110      libexe_mz_header_t **mz_header,
111      libcerror_error_t **error )
112 {
113 	static char *function = "libexe_mz_header_free";
114 
115 	if( mz_header == NULL )
116 	{
117 		libcerror_error_set(
118 		 error,
119 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
120 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
121 		 "%s: invalid MZ header.",
122 		 function );
123 
124 		return( -1 );
125 	}
126 	if( *mz_header != NULL )
127 	{
128 		memory_free(
129 		 *mz_header );
130 
131 		*mz_header = NULL;
132 	}
133 	return( 1 );
134 }
135 
136 /* Reads the MZ header
137  * Returns 1 if successful or -1 on error
138  */
libexe_mz_header_read_data(libexe_mz_header_t * mz_header,const uint8_t * data,size_t data_size,libcerror_error_t ** error)139 int libexe_mz_header_read_data(
140      libexe_mz_header_t *mz_header,
141      const uint8_t *data,
142      size_t data_size,
143      libcerror_error_t **error )
144 {
145 	static char *function                 = "libexe_mz_header_read_data";
146 	uint16_t number_of_relocation_entries = 0;
147 	uint16_t relocation_table_offset      = 0;
148 
149 #if defined( HAVE_DEBUG_OUTPUT )
150 	uint32_t value_32bit                  = 0;
151 	uint16_t value_16bit                  = 0;
152 #endif
153 
154 	if( mz_header == NULL )
155 	{
156 		libcerror_error_set(
157 		 error,
158 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
159 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
160 		 "%s: invalid MZ header.",
161 		 function );
162 
163 		return( -1 );
164 	}
165 	if( data == NULL )
166 	{
167 		libcerror_error_set(
168 		 error,
169 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
170 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
171 		 "%s: invalid data.",
172 		 function );
173 
174 		return( -1 );
175 	}
176 	if( data_size < sizeof( exe_mz_header_t ) )
177 	{
178 		libcerror_error_set(
179 		 error,
180 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
181 		 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
182 		 "%s: invalid data size value too small.",
183 		 function );
184 
185 		return( -1 );
186 	}
187 	if( data_size > (size_t) SSIZE_MAX )
188 	{
189 		libcerror_error_set(
190 		 error,
191 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
192 		 LIBCERROR_RUNTIME_ERROR_VALUE_EXCEEDS_MAXIMUM,
193 		 "%s: invalid data size value exceeds maximum.",
194 		 function );
195 
196 		return( -1 );
197 	}
198 #if defined( HAVE_DEBUG_OUTPUT )
199 	if( libcnotify_verbose != 0 )
200 	{
201 		libcnotify_printf(
202 		 "%s: MZ header:\n",
203 		 function );
204 		libcnotify_print_data(
205 		 data,
206 		 sizeof( exe_mz_header_t ),
207 		 0 );
208 	}
209 #endif
210 	if( memory_compare(
211 	     ( (exe_mz_header_t *) data )->signature,
212 	     EXE_MZ_SIGNATURE,
213 	     2 ) != 0 )
214 	{
215 		libcerror_error_set(
216 		 error,
217 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
218 		 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
219 		 "%s: invalid signature.",
220 		 function );
221 
222 		return( -1 );
223 	}
224 	byte_stream_copy_to_uint16_little_endian(
225 	 ( (exe_mz_header_t *) data )->number_of_relocation_entries,
226 	 number_of_relocation_entries );
227 
228 	byte_stream_copy_to_uint16_little_endian(
229 	 ( (exe_mz_header_t *) data )->relocation_table_offset,
230 	 relocation_table_offset );
231 
232 #if defined( HAVE_DEBUG_OUTPUT )
233 	if( libcnotify_verbose != 0 )
234 	{
235 		libcnotify_printf(
236 		 "%s: signature\t\t\t\t\t: %c%c\n",
237 		 function,
238 		 ( (exe_mz_header_t *) data )->signature[ 0 ],
239 		 ( (exe_mz_header_t *) data )->signature[ 1 ] );
240 
241 		byte_stream_copy_to_uint16_little_endian(
242 		 ( (exe_mz_header_t *) data )->last_page_size,
243 		 value_16bit );
244 		libcnotify_printf(
245 		 "%s: last page size\t\t\t\t: %" PRIu16 "\n",
246 		 function,
247 		 value_16bit );
248 
249 		byte_stream_copy_to_uint16_little_endian(
250 		 ( (exe_mz_header_t *) data )->number_of_pages,
251 		 value_16bit );
252 		libcnotify_printf(
253 		 "%s: number of pages\t\t\t\t: %" PRIu16 "\n",
254 		 function,
255 		 value_16bit );
256 
257 		libcnotify_printf(
258 		 "%s: number of relocation entries\t\t: %" PRIu16 "\n",
259 		 function,
260 		 number_of_relocation_entries );
261 
262 		byte_stream_copy_to_uint16_little_endian(
263 		 ( (exe_mz_header_t *) data )->number_of_header_paragraphs,
264 		 value_16bit );
265 		libcnotify_printf(
266 		 "%s: number of header paragraphs\t\t\t: %" PRIu16 "\n",
267 		 function,
268 		 value_16bit );
269 
270 		byte_stream_copy_to_uint16_little_endian(
271 		 ( (exe_mz_header_t *) data )->minimum_allocated_paragraphs,
272 		 value_16bit );
273 		libcnotify_printf(
274 		 "%s: minimum allocated paragraphs\t\t: %" PRIu16 "\n",
275 		 function,
276 		 value_16bit );
277 
278 		byte_stream_copy_to_uint16_little_endian(
279 		 ( (exe_mz_header_t *) data )->maximum_allocated_paragraphs,
280 		 value_16bit );
281 		libcnotify_printf(
282 		 "%s: maximum allocated paragraphs\t\t: %" PRIu16 "\n",
283 		 function,
284 		 value_16bit );
285 
286 		byte_stream_copy_to_uint16_little_endian(
287 		 ( (exe_mz_header_t *) data )->initial_stack_segment,
288 		 value_16bit );
289 		libcnotify_printf(
290 		 "%s: initial stack segment\t\t\t: 0x%04" PRIx16 "\n",
291 		 function,
292 		 value_16bit );
293 
294 		byte_stream_copy_to_uint16_little_endian(
295 		 ( (exe_mz_header_t *) data )->initial_stack_pointer,
296 		 value_16bit );
297 		libcnotify_printf(
298 		 "%s: initial stack pointer\t\t\t: 0x%04" PRIx16 "\n",
299 		 function,
300 		 value_16bit );
301 
302 		byte_stream_copy_to_uint16_little_endian(
303 		 ( (exe_mz_header_t *) data )->checksum,
304 		 value_16bit );
305 		libcnotify_printf(
306 		 "%s: checksum\t\t\t\t\t: 0x%04" PRIx16 "\n",
307 		 function,
308 		 value_16bit );
309 
310 		byte_stream_copy_to_uint32_little_endian(
311 		 ( (exe_mz_header_t *) data )->entry_point,
312 		 value_32bit );
313 		libcnotify_printf(
314 		 "%s: entry point\t\t\t\t\t: 0x%08" PRIx32 "\n",
315 		 function,
316 		 value_32bit );
317 
318 		libcnotify_printf(
319 		 "%s: relocation table offset\t\t\t: 0x%04" PRIx16 "\n",
320 		 function,
321 		 relocation_table_offset );
322 
323 		byte_stream_copy_to_uint16_little_endian(
324 		 ( (exe_mz_header_t *) data )->overlay_number,
325 		 value_16bit );
326 		libcnotify_printf(
327 		 "%s: overlay number\t\t\t\t: %" PRIu16 "\n",
328 		 function,
329 		 value_16bit );
330 
331 		libcnotify_printf(
332 		 "\n" );
333 	}
334 #endif /* defined( HAVE_DEBUG_OUTPUT ) */
335 
336 	if( relocation_table_offset >= 0x40 )
337 	{
338 /* TODO read data */
339 		byte_stream_copy_to_uint32_little_endian(
340 		 ( (exe_mz_header_t *) data )->extended_header_offset,
341 		 mz_header->extended_header_offset );
342 
343 #if defined( HAVE_DEBUG_OUTPUT )
344 		if( libcnotify_verbose != 0 )
345 		{
346 			libcnotify_printf(
347 			 "%s: unknown1:\n",
348 			 function );
349 			libcnotify_print_data(
350 			 ( (exe_mz_header_t *) data )->unknown1,
351 			 32,
352 			 0 );
353 
354 			libcnotify_printf(
355 			 "%s: extended header offset\t\t\t: 0x%08" PRIx32 "\n",
356 			 function,
357 			 mz_header->extended_header_offset );
358 
359 			libcnotify_printf(
360 			 "%s: unknown2:\n",
361 			 function );
362 			libcnotify_print_data(
363 			 ( (exe_mz_header_t *) data )->unknown2,
364 			 112,
365 			 0 );
366 		}
367 #endif /* defined( HAVE_DEBUG_OUTPUT ) */
368 	}
369 /* TODO print data between realloc and current offset */
370 	if( number_of_relocation_entries > 0 )
371 	{
372 /* TODO print relation table entries */
373 	}
374 	return( 1 );
375 }
376 
377 /* Reads the MZ header from a Basic File IO (bfio) handle
378  * Returns 1 if successful or -1 on error
379  */
libexe_mz_header_read_file_io_handle(libexe_mz_header_t * mz_header,libbfio_handle_t * file_io_handle,off64_t file_offset,libcerror_error_t ** error)380 int libexe_mz_header_read_file_io_handle(
381      libexe_mz_header_t *mz_header,
382      libbfio_handle_t *file_io_handle,
383      off64_t file_offset,
384      libcerror_error_t **error )
385 {
386 	uint8_t data[ sizeof( exe_mz_header_t ) ];
387 
388 	static char *function = "libexe_mz_header_read_file_io_handle";
389 	ssize_t read_count    = 0;
390 
391 #if defined( HAVE_DEBUG_OUTPUT )
392 	if( libcnotify_verbose != 0 )
393 	{
394 		libcnotify_printf(
395 		 "%s: reading MZ header at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
396 		 function,
397 		 file_offset,
398 		 file_offset );
399 	}
400 #endif
401 	read_count = libbfio_handle_read_buffer_at_offset(
402 	              file_io_handle,
403 	              data,
404 	              sizeof( exe_mz_header_t ),
405 	              file_offset,
406 	              error );
407 
408 	if( read_count != (ssize_t) sizeof( exe_mz_header_t ) )
409 	{
410 		libcerror_error_set(
411 		 error,
412 		 LIBCERROR_ERROR_DOMAIN_IO,
413 		 LIBCERROR_IO_ERROR_READ_FAILED,
414 		 "%s: unable to read MZ header data at offset: %" PRIi64 " (0x%08" PRIx64 ").",
415 		 function,
416 		 file_offset,
417 		 file_offset );
418 
419 		return( -1 );
420 	}
421 	if( libexe_mz_header_read_data(
422 	     mz_header,
423 	     data,
424 	     sizeof( exe_mz_header_t ),
425 	     error ) != 1 )
426 	{
427 		libcerror_error_set(
428 		 error,
429 		 LIBCERROR_ERROR_DOMAIN_IO,
430 		 LIBCERROR_IO_ERROR_READ_FAILED,
431 		 "%s: unable to read MZ header at offset: %" PRIi64 " (0x%08" PRIx64 ").",
432 		 function,
433 		 file_offset,
434 		 file_offset );
435 
436 		return( -1 );
437 	}
438 	return( 1 );
439 }
440 
441