#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>
#include "peexe.h"

#define MAP_ANONYMOUS 0x20

struct mz_header_s
{
    u_char dont_care1[0x18];	/* MZ Header stuff			*/
    u_char must_be_0x40;	/* 0x40 for non-MZ program		*/
    u_char dont_care2[0x23];	/* More MZ header stuff			*/
    u_short ne_offset;		/* Offset to extended header		*/
};

struct mz_header_s mz;
struct pe_header_s pe;
struct pe_segment_table * pe_seg;
unsigned int load_addr;

struct PE_Import_Directory * pe_import;
struct PE_Export_Directory * pe_export;
struct PE_Resource_Directory * pe_resource;

FILE * infile;

static void * read_segment(struct pe_segment_table * seg)
{
  int status;
  void * retval;

  retval = (void *) malloc(seg->Size_Of_Raw_Data);
  status = fseek(infile, seg->PointerToRawData, SEEK_SET);
  status = fread (retval, seg->Size_Of_Raw_Data, 1,  infile);
  return retval;
}

void my_wcstombs(char * result, u_short * source, int len)
{
  while(len--) {
    if(isascii(*source)) *result++ = *source++;
    else {
      printf("Unable to handle unicode right now\n");
      exit(0);
    }
  };
}

char * xmmap(char * vaddr, unsigned int v_size, int prot, int flags, 
	     int fd, unsigned int file_offset)
{
  char * result;
  result = mmap(vaddr, v_size, prot, flags, fd, file_offset);
  if((unsigned int) result != 0xffffffff) return result;

  /* Sigh.  Alignment must be wrong for mmap.  Do this the hard way. */
  if(!(flags & MAP_FIXED)) {
    vaddr = 0x40000000;
    flags |= MAP_FIXED;
  };

  mmap(vaddr, v_size, prot, MAP_ANONYMOUS | flags, 0, 0);
  lseek(fd, file_offset, SEEK_SET);
  read(fd, vaddr, v_size);
  return vaddr;
};

dump_resource_entry(struct PE_Directory_Entry * PE_de, int level)
{
  int i;
  struct PE_Directory_Name_String_U * u_name;
  struct PE_Resource_Leaf_Entry * PE_le;
  char * cur_data_entry;
  char res_name[256];

  if(PE_de->Name & IMAGE_RESOURCE_NAME_IS_STRING) {
    u_name =  (struct PE_Directory_Name_String_U * ) ((char *) pe_resource + 
	   (PE_de->Name & ~IMAGE_RESOURCE_NAME_IS_STRING));
    memset(res_name, 0, sizeof(res_name));
    my_wcstombs(res_name, u_name->NameString, u_name->Length);
  } else {
    sprintf(res_name,"%8.8xh", PE_de->Name);
  }

  cur_data_entry = ((char *) pe_resource + 
		 (PE_de->OffsetToData & ~IMAGE_RESOURCE_NAME_IS_STRING));

  for(i=0; i<level; i++) printf("   ");
  if(PE_de->OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY) {
    printf("New level %s\n", res_name);
    dump_resource((struct PE_Resource_directory *) cur_data_entry, 
		  level + 1);
  } else {
    PE_le = (struct PE_Resource_Leaf_Entry *) cur_data_entry;
    printf("%s %x %x %x\n", res_name, PE_le->OffsetToData, PE_le->Size,
	   PE_le->CodePage);
  }
}

dump_resource(struct PE_Resource_Directory * pe_res, int level)
{
  struct PE_Directory_Entry * PE_de;
  int i;

  if(level == 0) printf("\n************Resource Information *************\n");

  PE_de = (struct PE_Directory_Entry *) (pe_res + 1);

  for(i=0; i< pe_res->NumberOfNamedEntries; i++)
    dump_resource_entry(PE_de++, level);

  for(i=0; i< pe_res->NumberOfIdEntries; i++)
    dump_resource_entry(PE_de++, level);

  return;
}

dump_exports(struct PE_Export_Directory * pe_exports)
{ 
  char * Module;
  int i;
  u_short * ordinal;
  u_long * function;
  u_char ** name, *ename;

  Module = ((char *) load_addr) + pe_exports->Name;
  printf("\n*******EXPORT DATA*******\nModule name is %s, %d functions, %d names\n", 
	 Module,
	 pe_exports->Number_Of_Functions,
	 pe_exports->Number_Of_Names);

  ordinal = (u_short *) (((char *) load_addr) + (int) pe_exports->Address_Of_Name_Ordinals);
  function = (u_long *)  (((char *) load_addr) + (int) pe_exports->AddressOfFunctions);
  name = (u_char **)  (((char *) load_addr) + (int) pe_exports->AddressOfNames);

  printf("%-32s Ordinal Virt Addr\n", "Function Name");
  for(i=0; i< pe_exports->Number_Of_Functions; i++)
    {
      ename =  (char *) (((char *) load_addr) + (int) *name++);
      printf("%-32s %4d    %8.8x\n", ename, *ordinal++, *function++);
    }

  return;
}

dump_imports(struct PE_Import_Directory * pe_imports)
{ 
  struct PE_Import_Directory * pe_imp;

 /* OK, now dump the import list */
  printf("\nDumping imports list\n");
  pe_imp = pe_import;
  while (pe_imp->ModuleName)
    {
      char * Module, *Function;
      struct pe_import_name * pe_name;
      unsigned int * import_list;
      char * c;

      Module = ((char *) load_addr) + pe_imp->ModuleName;
      printf("%s\n", Module);
      c = strchr(Module, '.');
      if (c) *c = 0;

      import_list = (unsigned int *) 
	(((unsigned int) load_addr) + pe_imp->Import_List);

      while(*import_list)
	{
	  pe_name = (struct pe_import_name *) ((int) load_addr + *import_list);
	  printf("--- %s %s.%d\n", pe_name->Name, Module, pe_name->Hint);
	  import_list++;
	}
      pe_imp++;
    };
}

main(int argc, char * argv[])
{
  int status, i;
  int idata_seg;
  char * result;
 if(argc < 2) exit(0);

  infile = fopen(argv[1], "r");
  if(!infile) exit(0);

  fread(&mz, sizeof(mz), 1, infile);

  status = fseek(infile, mz.ne_offset, SEEK_SET);

  status = fread(&pe, sizeof(pe), 1, infile);
  /* OK, now we have the pe header.  Next read in the section table */

  pe_seg = (struct pe_segment_table *) malloc(sizeof(struct pe_segment_table) * pe.coff.NumberOfSections);

  status = fread (pe_seg, sizeof(struct pe_segment_table) * 
		  pe.coff.NumberOfSections, 1,  infile);

  /* Now get the import directory */

  /* Now dump the sections */
  printf("Dump of segment table\n");
  printf("   Name    VSz  Vaddr     SzRaw   Fileadr  *Reloc *Lineum #Reloc #Linum Char\n");
  for(i=0; i<pe.coff.NumberOfSections; i++)
    {
      printf("%8s: %4.4x %8.8x %8.8x %8.8x %8.8x %8.8x %4.4x %4.4x %8.8x\n", 
	     pe_seg[i].Name, 
	     pe_seg[i].Virtual_Size,
	     pe_seg[i].Virtual_Address,
	     pe_seg[i].Size_Of_Raw_Data,
	     pe_seg[i].PointerToRawData,
	     pe_seg[i].PointerToRelocations,
	     pe_seg[i].PointerToLinenumbers,
	     pe_seg[i].NumberOfRelocations,
	     pe_seg[i].NumberOfLinenumbers,
	     pe_seg[i].Characteristics);
      if(!load_addr) {
	result = xmmap(0, pe_seg[i].Size_Of_Raw_Data, 7, MAP_PRIVATE, 
		      fileno(infile), pe_seg[i].PointerToRawData);
	load_addr = (unsigned int) result -  pe_seg[i].Virtual_Address;
      } else {
	result = xmmap((char *) load_addr + pe_seg[i].Virtual_Address, 
		      pe_seg[i].Size_Of_Raw_Data, 7, MAP_PRIVATE | MAP_FIXED, 
		      fileno(infile), pe_seg[i].PointerToRawData);
      }

      if(strcmp(pe_seg[i].Name, ".idata") == 0)
	pe_import = (struct PE_Import_Directory *) result;

      if(strcmp(pe_seg[i].Name, ".edata") == 0)
	pe_export = (struct PE_Export_Directory *) result;

      if(strcmp(pe_seg[i].Name, ".rsrc") == 0)
	pe_resource = (struct PE_Resource_Directory *) result;
    }

  if(pe_import) dump_imports(pe_import);
  if(pe_export) dump_exports(pe_export);
  if(pe_resource) dump_resource(pe_resource, 0);
}
