1 /*
2  *	HT Editor
3  *	htpeimp.cc
4  *
5  *	Copyright (C) 1999-2002 Stefan Weyergraf
6  *
7  *	This program is free software; you can redistribute it and/or modify
8  *	it under the terms of the GNU General Public License version 2 as
9  *	published by the Free Software Foundation.
10  *
11  *	This program is distributed in the hope that it will be useful,
12  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *	GNU General Public License for more details.
15  *
16  *	You should have received a copy of the GNU General Public License
17  *	along with this program; if not, write to the Free Software
18  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 #include "formats.h"
22 #include "htanaly.h"
23 #include "htctrl.h"
24 #include "data.h"
25 #include "endianess.h"
26 #include "htiobox.h"
27 #include "htnewexe.h"
28 #include "htpal.h"
29 #include "htpe.h"
30 #include "htpeimp.h"
31 #include "stream.h"
32 #include "strtools.h"
33 #include "httag.h"
34 #include "log.h"
35 #include "pe_analy.h"
36 #include "snprintf.h"
37 #include "tools.h"
38 
39 #include <stdlib.h>
40 #include <string.h>
41 
htpeimports_init(Bounds * b,File * file,ht_format_group * group)42 static ht_view *htpeimports_init(Bounds *b, File *file, ht_format_group *group)
43 {
44 	ht_pe_shared_data *pe_shared=(ht_pe_shared_data *)group->get_shared_data();
45 
46 	if (pe_shared->opt_magic!=COFF_OPTMAGIC_PE32 && pe_shared->opt_magic!=COFF_OPTMAGIC_PE64) return NULL;
47 
48 	bool pe32 = (pe_shared->opt_magic==COFF_OPTMAGIC_PE32);
49 
50 	uint32 sec_rva, sec_size;
51 	if (pe32) {
52 		sec_rva = pe_shared->pe32.header_nt.directory[PE_DIRECTORY_ENTRY_IMPORT].address;
53 		sec_size = pe_shared->pe32.header_nt.directory[PE_DIRECTORY_ENTRY_IMPORT].size;
54 	} else {
55 		sec_rva = pe_shared->pe64.header_nt.directory[PE_DIRECTORY_ENTRY_IMPORT].address;
56 		sec_size = pe_shared->pe64.header_nt.directory[PE_DIRECTORY_ENTRY_IMPORT].size;
57 	}
58 	if (!sec_rva || !sec_size) return NULL;
59 
60 	int h0=new_timer();
61 	start_timer(h0);
62 
63 	ht_group *g;
64 	Bounds c;
65 	String fn, s, dllname;
66 
67 	c=*b;
68 	g=new ht_group();
69 	g->init(&c, VO_RESIZE, DESC_PE_IMPORTS"-g");
70 	ht_statictext *head;
71 
72 	int dll_count=0;
73 	int function_count=0;
74 
75 	c.y++;
76 	c.h--;
77 	ht_pe_import_viewer *v=new ht_pe_import_viewer();
78 	v->init(&c, DESC_PE_IMPORTS, group);
79 
80 	c.y--;
81 	c.h=1;
82 
83 	PE_IMPORT_DESCRIPTOR import;
84 	FileOfs dofs;
85 	uint dll_index;
86 	char iline[256];
87 
88 	/* get import directory offset */
89 	/* 1. get import directory rva */
90 	FileOfs iofs;
91 	RVA irva;
92 	uint isize;
93 	if (pe32) {
94 		irva = pe_shared->pe32.header_nt.directory[PE_DIRECTORY_ENTRY_IMPORT].address;
95 		isize = pe_shared->pe32.header_nt.directory[PE_DIRECTORY_ENTRY_IMPORT].size;
96 	} else {
97 		irva = pe_shared->pe64.header_nt.directory[PE_DIRECTORY_ENTRY_IMPORT].address;
98 		isize = pe_shared->pe64.header_nt.directory[PE_DIRECTORY_ENTRY_IMPORT].size;
99 	}
100 	/* 2. transform it into an offset */
101 	if (!pe_rva_to_ofs(&pe_shared->sections, irva, &iofs)) goto pe_read_error;
102 	LOG("%y: PE: reading import directory at offset 0x%08qx, rva 0x%08x, size 0x%08x...", &file->getFilename(fn), iofs, irva, isize);
103 
104 	/* make a memfile out of the whole file */
105 
106 	/* doesn't work because import information is NOT contained into
107 	   the import directory !!! only the import dir header. (I !love M$) */
108 
109 	/*** read import directory ***/
110 	dofs = iofs;
111 	dll_index = 0;
112 
113 	while (1) {
114 		file->seek(dofs);
115 		file->readx(&import, sizeof import);
116 		createHostStruct(&import, PE_IMPORT_DESCRIPTOR_struct, little_endian);
117 		if ((!import.characteristics) && (!import.name)) break;
118 		dofs = file->tell();
119 		/* get name of dll */
120 		FileOfs iname_ofs;
121 		if (!pe_rva_to_ofs(&pe_shared->sections, import.name, &iname_ofs)) {
122 			if (import.name >= file->getSize()) goto pe_read_error;
123 			file->seek(import.name);
124 		} else {
125 			if (iname_ofs >= file->getSize()) goto pe_read_error;
126 			file->seek(iname_ofs);
127 		}
128 		file->readStringz(dllname);
129 		dll_count++;
130 
131 		/*** imported functions by name or by ordinal ***/
132 
133 		/*
134 		 *	First thunk (FT)
135 		 *	The first thunk table will be overwritten by the system/loader with
136 		 *	the function entry-points. So the program will treat this table
137 		 *	as if it contained just imported addresses.
138 		 */
139 		RVA fthunk_rva = import.first_thunk;
140 		FileOfs fthunk_ofs;
141 		if (!pe_rva_to_ofs(&pe_shared->sections, fthunk_rva, &fthunk_ofs)) goto pe_read_error;
142 		/*
143 		 *	...and Original First Thunk (OFT)
144 		 * 	I saw executables that have the OFT ptr set to 0 and seem to
145 		 *	use the FT ptr instead, but also some that have both !=0 and use
146 		 *	the original one (bound executables).
147 		 */
148 		RVA thunk_rva;
149 		FileOfs thunk_ofs;
150 		if (import.original_first_thunk) {
151 			thunk_rva = import.original_first_thunk;
152 			if (!pe_rva_to_ofs(&pe_shared->sections, thunk_rva, &thunk_ofs)) goto pe_read_error;
153 		} else {
154 			thunk_rva = fthunk_rva;
155 			thunk_ofs = fthunk_ofs;
156 		}
157 		ht_pe_import_library *lib=new ht_pe_import_library(dllname.contentChar());
158 		pe_shared->imports.libs->insert(lib);
159 
160 		PE_THUNK_DATA thunk;
161 		PE_THUNK_DATA_64 thunk64;
162 		uint thunk_count = 0;
163 		file->seek(thunk_ofs);
164 		while (1) {
165 			if (pe32) {
166 				file->readx(&thunk, sizeof thunk);
167 				createHostStruct(&thunk, PE_THUNK_DATA_struct, little_endian);
168 				if (!thunk.ordinal) break;
169 			} else {
170 				file->readx(&thunk64, sizeof thunk64);
171 				createHostStruct(&thunk64, PE_THUNK_DATA_64_struct, little_endian);
172 				if (!thunk64.ordinal) break;
173 			}
174 			thunk_count++;
175 		}
176 
177 		PE_THUNK_DATA *thunk_table = NULL;
178 		PE_THUNK_DATA_64 *thunk_table64 = NULL;
179 		file->seek(thunk_ofs);
180 		if (thunk_count) {
181 			if (pe32) {
182 				thunk_table = ht_malloc(sizeof *thunk_table * thunk_count);
183 				file->readx(thunk_table, sizeof *thunk_table * thunk_count);
184 				// FIXME: ?
185 				for (uint i=0; i<thunk_count; i++) {
186 					createHostStruct(thunk_table+i, PE_THUNK_DATA_struct, little_endian);
187 				}
188 			} else {
189 				thunk_table64 = ht_malloc(sizeof *thunk_table64 * thunk_count);
190 				file->readx(thunk_table64, sizeof *thunk_table64 * thunk_count);
191 				// FIXME: ?
192 				for (uint i=0; i<thunk_count; i++) {
193 					createHostStruct(thunk_table64+i, PE_THUNK_DATA_64_struct, little_endian);
194 				}
195 			}
196 		}
197 		for (uint32 i=0; i<thunk_count; i++) {
198 			function_count++;
199 			ht_pe_import_function *func;
200 			/* follow (original) first thunk */
201 			if (pe32) {
202 				thunk = thunk_table[i];
203 
204 				if (thunk.ordinal & 0x80000000) {
205 					/* by ordinal */
206 					func = new ht_pe_import_function(dll_index, fthunk_rva, thunk.ordinal&0xffff);
207 				} else {
208 					/* by name */
209 					FileOfs function_desc_ofs;
210 					uint16 hint = 0;
211 					if (pe_rva_to_ofs(&pe_shared->sections, thunk.function_desc_address, &function_desc_ofs)) {
212 						if (function_desc_ofs >= file->getSize()) goto pe_read_error;
213 						file->seek(function_desc_ofs);
214 					} else {
215 						if (thunk.function_desc_address >= file->getSize()) goto pe_read_error;
216 						file->seek(thunk.function_desc_address);
217 					}
218 					file->readx(&hint, 2);
219 					hint = createHostInt(&hint, 2, little_endian);
220 					file->readStringz(s);
221 					func = new ht_pe_import_function(dll_index, fthunk_rva, s.contentChar(), hint);
222 				}
223 			} else {
224 				thunk64 = thunk_table64[i];
225 
226 				// FIXME: is this correct ?
227 				if (thunk64.ordinal & 0x8000000000000000ULL) {
228 					/* by ordinal */
229 					func = new ht_pe_import_function(dll_index, fthunk_rva, thunk64.ordinal & 0xffff);
230 				} else {
231 					/* by name */
232 					FileOfs function_desc_ofs;
233 					uint16 hint = 0;
234 					if (!pe_rva_to_ofs(&pe_shared->sections, thunk64.function_desc_address, &function_desc_ofs)) goto pe_read_error;
235 					file->seek(function_desc_ofs);
236 					file->readx(&hint, 2);
237 					hint = createHostInt(&hint, 2, little_endian);
238 					file->readStringz(s);
239 					func = new ht_pe_import_function(dll_index, fthunk_rva, s.contentChar(), hint);
240 				}
241 			}
242 			pe_shared->imports.funcs->insert(func);
243 
244 			if (pe32) {
245 				thunk_ofs+=4;
246 				thunk_rva+=4;
247 				fthunk_ofs+=4;
248 				fthunk_rva+=4;
249 			} else {
250 				thunk_ofs+=8;
251 				thunk_rva+=8;
252 				fthunk_ofs+=8;
253 				fthunk_rva+=8;
254 			}
255 		}
256 
257 		dll_index++;
258 
259 		if (pe32) {
260 			free(thunk_table);
261 		} else {
262 			free(thunk_table64);
263 		}
264 	}
265 
266 	stop_timer(h0);
267 //	LOG("%y: PE: %d ticks (%d msec) to read imports", file->get_name(), get_timer_tick(h0), get_timer_msec(h0));
268 	delete_timer(h0);
269 
270 	ht_snprintf(iline, sizeof iline, "* PE import directory at offset %08qx (%d functions from %d libraries)", iofs, function_count, dll_count);
271 	head=new ht_statictext();
272 	head->init(&c, iline, align_left);
273 
274 	g->insert(head);
275 	g->insert(v);
276 	//
277 	for (uint i=0; i<pe_shared->imports.funcs->count(); i++) {
278 		ht_pe_import_function *func = (ht_pe_import_function*)(*pe_shared->imports.funcs)[i];
279 		assert(func);
280 		ht_pe_import_library *lib = (ht_pe_import_library*)(*pe_shared->imports.libs)[func->libidx];
281 		assert(lib);
282 		char addr[32], name[256];
283 		ht_snprintf(addr, sizeof addr, "%08x", func->address);
284 		if (func->byname) {
285 			ht_snprintf(name, sizeof name, "%s", func->name.name);
286 		} else {
287 			ht_snprintf(name, sizeof name, "%04x (by ordinal)", func->ordinal);
288 		}
289 		v->insert_str(i, lib->name, addr, name);
290 	}
291 	//
292 	v->update();
293 
294 	g->setpalette(palkey_generic_window_default);
295 
296 	pe_shared->v_imports=v;
297 	return g;
298 pe_read_error:
299 	delete_timer(h0);
300 	errorbox("%y: PE import section seems to be corrupted.", &file->getFilename(fn));
301 	g->done();
302 	delete g;
303 	v->done();
304 	delete v;
305 	return NULL;
306 }
307 
308 format_viewer_if htpeimports_if = {
309 	htpeimports_init,
310 	NULL
311 };
312 
313 /*
314  *	class ht_pe_import_library
315  */
316 
ht_pe_import_library(const char * n)317 ht_pe_import_library::ht_pe_import_library(const char *n)
318 {
319 	name = ht_strdup(n);
320 }
321 
~ht_pe_import_library()322 ht_pe_import_library::~ht_pe_import_library()
323 {
324 	free(name);
325 }
326 
327 /*
328  *	class ht_pe_import_function
329  */
330 
ht_pe_import_function(uint li,RVA a,uint o)331 ht_pe_import_function::ht_pe_import_function(uint li, RVA a, uint o)
332 {
333 	libidx = li;
334 	ordinal = o;
335 	address = a;
336 	byname = false;
337 }
338 
ht_pe_import_function(uint li,RVA a,const char * n,uint h)339 ht_pe_import_function::ht_pe_import_function(uint li, RVA a, const char *n, uint h)
340 {
341 	libidx= li;
342 	name.name = ht_strdup(n);
343 	name.hint = h;
344 	address = a;
345 	byname = true;
346 }
347 
~ht_pe_import_function()348 ht_pe_import_function::~ht_pe_import_function()
349 {
350 	if (byname) free(name.name);
351 }
352 
353 /*
354  *	CLASS ht_pe_import_viewer
355  */
356 
init(Bounds * b,const char * Desc,ht_format_group * fg)357 void	ht_pe_import_viewer::init(Bounds *b, const char *Desc, ht_format_group *fg)
358 {
359 	ht_text_listbox::init(b, 3, 2, LISTBOX_QUICKFIND);
360 	options |= VO_BROWSABLE;
361 	desc = strdup(Desc);
362 	format_group = fg;
363 	grouplib = false;
364 	sortby = 1;
365 	dosort();
366 }
367 
done()368 void	ht_pe_import_viewer::done()
369 {
370 	ht_text_listbox::done();
371 }
372 
dosort()373 void ht_pe_import_viewer::dosort()
374 {
375 	ht_text_listbox_sort_order sortord[2];
376 	uint l, s;
377 	if (grouplib) {
378 		l = 0;
379 		s = 1;
380 	} else {
381 		l = 1;
382 		s = 0;
383 	}
384 	sortord[l].col = 0;
385 	sortord[l].compare_func = strcmp;
386 	sortord[s].col = sortby;
387 	sortord[s].compare_func = strcmp;
388 	sort(2, sortord);
389 }
390 
func(uint i,bool execute)391 const char *ht_pe_import_viewer::func(uint i, bool execute)
392 {
393 	switch (i) {
394 		case 2:
395 			if (execute) {
396 				grouplib = !grouplib;
397 				dosort();
398 			}
399 			return grouplib ? (char*)"nbylib" : (char*)"bylib";
400 		case 4:
401 			if (execute) {
402 				if (sortby != 1) {
403 					sortby = 1;
404 					dosort();
405 				}
406 			}
407 			return "byaddr";
408 		case 5:
409 			if (execute) {
410 				if (sortby != 2) {
411 					sortby = 2;
412 					dosort();
413 				}
414 			}
415 			return "byname";
416 	}
417 	return NULL;
418 }
419 
handlemsg(htmsg * msg)420 void ht_pe_import_viewer::handlemsg(htmsg *msg)
421 {
422 	switch (msg->msg) {
423 	case msg_funcexec:
424 		if (func(msg->data1.integer, 1)) {
425 			clearmsg(msg);
426 			return;
427 		}
428 		break;
429 	case msg_funcquery: {
430 		const char *s=func(msg->data1.integer, 0);
431 		if (s) {
432 			msg->msg=msg_retval;
433 			msg->data1.cstr=s;
434 		}
435 		break;
436 	}
437 	case msg_keypressed: {
438 		if (msg->data1.integer == K_Return) {
439 			select_entry(e_cursor);
440 			clearmsg(msg);
441 		}
442 		break;
443 	}
444 	}
445 	ht_text_listbox::handlemsg(msg);
446 }
447 
select_entry(void * entry)448 bool ht_pe_import_viewer::select_entry(void *entry)
449 {
450 	ht_text_listbox_item *i = (ht_text_listbox_item *)entry;
451 
452 	ht_pe_shared_data *pe_shared=(ht_pe_shared_data *)format_group->get_shared_data();
453 
454 	ht_pe_import_function *e = (ht_pe_import_function*)(*pe_shared->imports.funcs)[i->id];
455 	if (!e) return true;
456 	if (pe_shared->v_image) {
457 		ht_aviewer *av = (ht_aviewer*)pe_shared->v_image;
458 		PEAnalyser *a = (PEAnalyser*)av->analy;
459 		Address *addr;
460 		if (pe_shared->opt_magic == COFF_OPTMAGIC_PE32) {
461 			addr = a->createAddress32(e->address+pe_shared->pe32.header_nt.image_base);
462 		} else {
463 			addr = a->createAddress64(e->address+pe_shared->pe64.header_nt.image_base);
464 		}
465 		if (av->gotoAddress(addr, NULL)) {
466 			app->focus(av);
467 			vstate_save();
468 		} else {
469 			global_analyser_address_string_format = ADDRESS_STRING_FORMAT_COMPACT | ADDRESS_STRING_FORMAT_ADD_0X;
470 			errorbox("can't follow: %s %y is not valid!", "import address", addr);
471 		}
472 		delete addr;
473 	} else errorbox("can't follow: no image viewer");
474 	return true;
475 }
476 
477 
478