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