How to thange ordinary EXE file into a DLL
PeRESec source

by Tomasz Lis, Lebork 2008-2009


Note that the code inserted here may be an older version of PERESec. Try searching around the site (or just ask me) for more recent version. At the moment I'm writing this, the most recent version is available on KeeperFX SVN repository.

PE/DLL Rebuilder of Export Section - peresec.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include "peresec_private.h"

// Maximum size of function name from input .MAP file.
// Used to verify the input file sanity.
#define MAX_EXPORT_NAMELEN 255
// Maximal amount of exported functions.
// Used to allocate static arrays.
#define MAX_EXPORTS 0x7fff
// Name of the export section to overwrite.
const char export_section_name[]=".edata";

// Text added at end of export section; just for fun
const char export_end_str[] ="Blessed are those who have not seen and yet believe";

// Sections
#define MAX_SECTIONS_NUM 64
#define MAX_SECTION_NAME_LEN 8
unsigned long sections_vaddr[MAX_SECTIONS_NUM];
unsigned long sections_raddr[MAX_SECTIONS_NUM];
unsigned long sections_rsize[MAX_SECTIONS_NUM];
unsigned char sections_names[MAX_SECTIONS_NUM][MAX_SECTION_NAME_LEN+1];

#define EXPORT_DIRECTORY_SIZE   0x0028
#define MZ_SIZEOF_HEADER        0x0040
#define MZ_NEWHEADER_OFS        0x003C
#define PE_SIZEOF_SIGNATURE     4
#define PE_SIZEOF_FILE_HEADER   20
#define PE_NUM_SECTIONS_OFS     2
#define PE_SIZEOF_OPTN_HEADER   96
#define PE_NUM_RVAS_AND_SIZES   92
#define PE_SIZEOF_DATADIR_ENTRY 8
#define PE_SIZEOF_SECTHDR_ENTRY 40

#ifndef false
#define false 0
#endif
#ifndef true
#define true 1
#endif

struct export_entry {
       unsigned short seg;
       unsigned long offs;
       unsigned long nmoffs;
       char srcname[MAX_EXPORT_NAMELEN+1];
       char dstname[MAX_EXPORT_NAMELEN+1];
       };

void export_sort(struct export_entry *exp[],unsigned int exp_size)
{
   int sorted=false;
   unsigned int i;
   struct export_entry *pTemp;
   // Sort the strings in ascending order
   while(!sorted)
   {
     sorted = true;
     for (i=0; i < exp_size-1; i++)
       if(strcmp(exp[i]->dstname, exp[i+1]->dstname) > 0)
       {
         sorted = false;     // We were out of order
         // Swap pointers exp[i] and exp[i+1]
         pTemp = exp[i];
         exp[i] = exp[i+1];
         exp[i+1] = pTemp;
       } 
   }
}

int find_dupename(struct export_entry *exp[],unsigned int exp_size)
{
   int i;
   for (i = 0 ; i < exp_size-1 ; i++)
       if(stricmp(exp[i]->dstname, exp[i+1]->dstname) == 0)
       {
         return i;
       } 
   return -1;
}

int find_dupeoffs(struct export_entry *exp[],unsigned int exp_size)
{
   int i,k;
   for (i = 0 ; i < exp_size ; i++)
     for (k = 0 ; k < exp_size ; k++)
       if ((i!=k) && (exp[i]->seg==exp[k]->seg) && (exp[i]->offs==exp[k]->offs))
       {
         return i;
       } 
   return -1;
}

char *get_name_with_prefix(char *dname,char *sname,char *prefix)
{
  int name_begin;
  if (strlen(sname)<2)
  {
    strcpy(dname,prefix);
    int dname_begin=strlen(dname);
    strcpy(dname+dname_begin,sname);
    return dname;
  }
  char name0=sname[0];
  char name1=sname[1];
  if ((name1=='@')||(name1=='?'))
  {
    dname[0]=name0;
    dname[1]=name1;
    name_begin=2;
  } else
  if ((name0=='@')||(name0=='?'))
  {
    dname[0]=name0;
    name_begin=1;
  } else
  {
    name_begin=0;
  }
  strcpy(dname+name_begin,prefix);
  int dname_begin=strlen(dname);
  strncpy(dname+dname_begin,sname+name_begin,MAX_EXPORT_NAMELEN-dname_begin-1);
  dname[MAX_EXPORT_NAMELEN]='\0';
  return dname;
}

/**
 * Reads 2-byte little-endian number from given FILE.
 */
inline unsigned short read_int16_le_file (FILE *fp)
{
    unsigned short l;
    l = fgetc(fp);
    l += fgetc(fp)<<8;
    return l;
}

/**
 * Reads 4-byte little-endian number from given FILE.
 */
inline long read_int32_le_file (FILE *fp)
{
    long l;
    l = fgetc(fp);
    l += fgetc(fp)<<8;
    l += fgetc(fp)<<16;
    l += fgetc(fp)<<24;
    return l;
}

/**
 * Writes 2-byte little-endian number to given FILE.
 */
inline void write_int16_le_file (FILE *fp, unsigned short x)
{
    fputc ((int) (x&255), fp);
    fputc ((int) ((x>>8)&255), fp);
}

/**
 * Writes 4-byte little-endian number to given FILE.
 */
inline void write_int32_le_file (FILE *fp, unsigned long x)
{
    fputc ((int) (x&255), fp);
    fputc ((int) ((x>>8)&255), fp);
    fputc ((int) ((x>>16)&255), fp);
    fputc ((int) ((x>>24)&255), fp);
}

/**
 * Returns length of opened file.
 * Value -1 means error.
 */
inline long file_length_opened (FILE *fp)
{
    long length;
    long lastpos;
    
    if (fp==NULL)
      return -1;
    lastpos = ftell (fp);
    if (fseek(fp, 0, SEEK_END) != 0)
      return -1;
    length = ftell(fp);
    fseek(fp, lastpos, SEEK_SET);
    return length;
}

short show_usage(char *fname)
{
    printf("usage:\n");
    printf("    %s <filename> [fstart]\n", fname);
    printf("where <filename> should be without extension,\n");
    printf("      [fstart] may be used to add prefix to function names.\n");
    return 1;
}

int main(int argc, char *argv[])
{
  printf("\nPE/DLL Rebuilder of Export Section (PeRESec) %s\n",VER_STRING);
  printf("-------------------------------\n");
  if (argc<2)
  {
      show_usage(argv[0]);
      return 11;
  }
  char *module_name=argv[1];
  char *funcname_prefix="";
  if (argc>2)
    funcname_prefix=argv[2];
  static struct export_entry *exports[MAX_EXPORTS];
  int idx;
  for (idx=0;idx<MAX_EXPORTS;idx++)
      exports[idx]=NULL;
  int module_name_size=strlen(module_name)+1;
  while ((module_name_size%8)!=0) module_name_size++;
  char *filename;
  filename=malloc(module_name_size+4);
  if (filename==NULL)
  {
      printf("Memory allocation error!\n");
      return 4;
  }
  // Reading functions
  sprintf(filename,"%s.map",module_name);
  FILE *fhndl=fopen(filename,"rb");
  if (fhndl==NULL)
  {
    printf("Can't open '%s' file!\n",filename);
    free(filename);
    return 1;
  }
  idx=0;
  while (!feof(fhndl))
  {
    exports[idx]=malloc(sizeof(struct export_entry));
    if (exports[idx]==NULL)
    {
      printf("Memory allocation error!\n");
      free(filename);
      return 4;
    }
    int nread=fscanf(fhndl," %hx:%lx %255s",&(exports[idx]->seg),&(exports[idx]->offs),exports[idx]->srcname);
    if ((nread<3)||(strlen(exports[idx]->srcname)<1))
    {
      if ((nread<=0) && feof(fhndl))
      {
        free(exports[idx]);
        exports[idx]=NULL;
        break;
      }
      printf("Error reading entry %d!\n",idx);
      printf("Fix the .MAP file and then retry.\n");
      free(filename);
      return 2;
    } else
    {
      get_name_with_prefix(exports[idx]->dstname,exports[idx]->srcname,funcname_prefix);
      exports[idx]->nmoffs=0;
      idx++;
      if (idx>=MAX_EXPORTS)
      {
        printf("Too many exports in .MAP file!\n");
        printf("Strip the file or increase MAX_EXPORTS to fix this.\n");
        free(filename);
        return 3;
      }
    }
  }
  fclose(fhndl);
  unsigned long num_of_exports=idx;
  printf("Got %d entries from .MAP file.\n",num_of_exports);
  //Sorting functions
  export_sort(exports,num_of_exports);
  printf("Entries are now sorted in memory.\n");
  // Checking
  int dupidx;
  dupidx=find_dupename(exports,num_of_exports);
  if (dupidx>=0)
  {
    printf("Duplicate entry name found!\n");
    printf("Entry \"%s\" duplicates. Aborting.\n",exports[dupidx]->dstname);
    printf("Remove duplicated entry from .MAP file to fix this.\n");
    free(filename);
    return 7;
  }
  dupidx=find_dupeoffs(exports,num_of_exports);
  if (dupidx>=0)
  {
    printf("Duplicate entry offset found!\n");
    printf("Offset 0x%08lX duplicates. Aborting.\n",exports[dupidx]->offs);
    printf("Remove duplicated entry from .MAP file to fix this.\n");
    free(filename);
    return 8;
  }
  //Saving the entries
  sprintf(filename,"%s.dll",module_name);
  fhndl=fopen(filename,"r+b");
  if (fhndl==NULL)
  {
    printf("Can't open '%s' file!\n",filename);
    printf("Check if it is in correct directory, and is not read-only.\n");
    free(filename);
    return 5;
  }
  // Reading headers
  unsigned long pe_header_raw_pos;
  unsigned long num_of_sections;
  unsigned long num_of_rva_and_sizes;
  unsigned long section_headers_raw_pos;
  unsigned long rvas_and_sizes_raw_pos;
  unsigned long filesize=file_length_opened(fhndl);
  // Locating PE header
  fseek(fhndl,MZ_NEWHEADER_OFS,SEEK_SET);
  pe_header_raw_pos=read_int32_le_file(fhndl);
  if ((pe_header_raw_pos < MZ_SIZEOF_HEADER) || 
  (pe_header_raw_pos+PE_SIZEOF_SIGNATURE+PE_SIZEOF_FILE_HEADER+PE_SIZEOF_OPTN_HEADER > filesize))
  {
      printf("The .DLL file has no valid 'new header'!\n");
      free(filename);
      return 6;
  }
  // Reading num. of sections
  fseek(fhndl,pe_header_raw_pos+PE_SIZEOF_SIGNATURE+PE_NUM_SECTIONS_OFS,SEEK_SET);
  num_of_sections=read_int16_le_file(fhndl);
  if (num_of_sections>=MAX_SECTIONS_NUM)
  {
      printf("The .DLL file has too many sections!\n");
      printf("Increaseing MAX_SECTIONS_NUM to above %d will fix this.\n",num_of_sections);
      free(filename);
      return 7;
  }
  // Reading num. of RVAs and sizes
  rvas_and_sizes_raw_pos = pe_header_raw_pos+PE_SIZEOF_SIGNATURE+PE_SIZEOF_FILE_HEADER;
  fseek(fhndl,rvas_and_sizes_raw_pos+PE_NUM_RVAS_AND_SIZES,SEEK_SET);
  num_of_rva_and_sizes=read_int32_le_file(fhndl);
  rvas_and_sizes_raw_pos += PE_SIZEOF_OPTN_HEADER;
  section_headers_raw_pos = rvas_and_sizes_raw_pos + num_of_rva_and_sizes*PE_SIZEOF_DATADIR_ENTRY;
  // Reading section headers
  for (idx=0;idx<=num_of_sections;idx++)
  {
      memset(sections_names[idx],0,MAX_SECTION_NAME_LEN+1);
      sections_vaddr[idx]=0x0f000000;
      sections_rsize[idx]=0;
      sections_raddr[idx]=0x0f000000;
  }
  for (idx=0;idx<num_of_sections;idx++)
  {
      unsigned long sect_pos = section_headers_raw_pos + idx*PE_SIZEOF_SECTHDR_ENTRY;
      fseek(fhndl,sect_pos,SEEK_SET);
      // Read section name
      fread (sections_names[idx+1], 1, MAX_SECTION_NAME_LEN, fhndl );
      sections_names[idx+1][MAX_SECTION_NAME_LEN]='\0';
      // Read addresses and sizes
      read_int32_le_file(fhndl);
      sections_vaddr[idx+1]=read_int32_le_file(fhndl);
      sections_rsize[idx+1]=read_int32_le_file(fhndl);
      sections_raddr[idx+1]=read_int32_le_file(fhndl);
  }
  int export_section_index=-1;
  for (idx=0;idx<=num_of_sections;idx++)
  {
      if (stricmp(sections_names[idx],export_section_name)==0)
      { export_section_index=idx; break; }
  }
  if (export_section_index<1)
  {
      printf("Cannot locate entry '%s' in section headers!\n",export_section_name);
      printf("Create the export section, or rename it to fix the problem.\n");
      free(filename);
      return 8;
  }
  printf("Export section '%s' located at RAW %08Xh.\n",export_section_name,sections_raddr[export_section_index]);
  printf("Section size is %d bytes (%08Xh).\n",sections_rsize[export_section_index],sections_rsize[export_section_index]);

  // Offsets relative to position of export section; these are computed using
  // the constants defined above
  const unsigned long adress_table_ofs   = EXPORT_DIRECTORY_SIZE;
  const unsigned long ordinal_table_ofs  = EXPORT_DIRECTORY_SIZE + (num_of_exports)*4;
  const unsigned long fnnames_table_ofs  = EXPORT_DIRECTORY_SIZE + (num_of_exports)*4 + (num_of_exports)*2;
  const unsigned long module_namestr_ofs = EXPORT_DIRECTORY_SIZE + (num_of_exports)*4 + (num_of_exports)*2 + (num_of_exports)*4;
  const unsigned long func_namestr_ofs   = EXPORT_DIRECTORY_SIZE + (num_of_exports)*4 + (num_of_exports)*2 + (num_of_exports)*4 + module_name_size;

  // Computing offsets inside the export section
  unsigned long address_table_raw_pos  = sections_raddr[export_section_index]+adress_table_ofs;
  unsigned long ordinal_table_raw_pos  = sections_raddr[export_section_index]+ordinal_table_ofs;
  unsigned long fnnames_table_raw_pos  = sections_raddr[export_section_index]+fnnames_table_ofs;
  unsigned long module_namestr_raw_pos = sections_raddr[export_section_index]+module_namestr_ofs;
  unsigned long func_namestr_raw_pos   = sections_raddr[export_section_index]+func_namestr_ofs;
  long          max_namestrs_size      = sections_rsize[export_section_index]-func_namestr_ofs;
  long          arr_raw_to_rva         = sections_vaddr[export_section_index]-sections_raddr[export_section_index];

  if (max_namestrs_size < MAX_EXPORT_NAMELEN)
  {
      printf("Cannot put %d entries in small export section!\n",MAX_EXPORT_NAMELEN);
      printf("Cut the .MAP file or make bigger section to fix this.\n");
      free(filename);
      return 9;
  }

  // Writing module name
  fseek(fhndl,module_namestr_raw_pos,SEEK_SET);
  for (idx=0;idx<strlen(module_name);idx++)
      fputc(module_name[idx],fhndl);
  for (;idx<module_name_size;idx++)
      fputc('\0',fhndl);
  // Writing function names
  fseek(fhndl,func_namestr_raw_pos,SEEK_SET);
  for (idx=0;idx<num_of_exports;idx++)
  {
      if (exports[idx]==NULL) break;
      unsigned long nmpos=ftell(fhndl);
      char *name=exports[idx]->dstname;
      exports[idx]->nmoffs=nmpos+arr_raw_to_rva;
      if (nmpos+strlen(name) >= func_namestr_raw_pos+max_namestrs_size)
      {
        printf("Function names space exceeded on func. %d!\n",idx);
        printf("Cut the .MAP file or make bigger section to fix this.\n");
        exports[idx]=NULL;
        break;
      }
      fputs(name,fhndl);
      fputc('\0',fhndl);
  }
  unsigned long end_of_used_space=ftell(fhndl);
  long remain_bts=func_namestr_raw_pos+max_namestrs_size-ftell(fhndl)-strlen(export_end_str);
  if (remain_bts>=0)
  {
    while (remain_bts>0)
    {
      fputc('\0',fhndl);
      remain_bts--;
    }
    fputs(export_end_str,fhndl);
  }
  printf("Written %d function export names into .DLL.\n",idx);
  // Updating section header
  fseek(fhndl,sections_raddr[export_section_index],SEEK_SET);
  {
    // export flags
        write_int32_le_file(fhndl,0);
    // export table creation date
        time_t dtime; time(&dtime);
        write_int32_le_file(fhndl,dtime);
    // export table version
        write_int32_le_file(fhndl,0);
    // module name address
        write_int32_le_file(fhndl,arr_raw_to_rva+module_namestr_raw_pos);
    // ordinal base
        write_int32_le_file(fhndl,1);
    // number of functions
        write_int32_le_file(fhndl,idx);
    // number of names
        write_int32_le_file(fhndl,idx);
    // address of functions
        write_int32_le_file(fhndl,arr_raw_to_rva+address_table_raw_pos);
    // address of names
        write_int32_le_file(fhndl,arr_raw_to_rva+fnnames_table_raw_pos);
    // address of ordinals
        write_int32_le_file(fhndl,arr_raw_to_rva+ordinal_table_raw_pos);
  }
  printf("Export section header updated.\n");
  fseek(fhndl,ordinal_table_raw_pos,SEEK_SET);
  for (idx=0;idx<num_of_exports;idx++)
  {
      if (exports[idx]==NULL)
      {
        write_int16_le_file(fhndl,0);
      } else
      {
        write_int16_le_file(fhndl,idx);
      }
  }
  printf("Written %d function export ordinals into .DLL.\n",idx);
  fseek(fhndl,address_table_raw_pos,SEEK_SET);
  for (idx=0;idx<num_of_exports;idx++)
  {
      if (exports[idx]==NULL)
      {
        write_int32_le_file(fhndl,0);
      } else
      {
        unsigned long val=exports[idx]->offs+sections_vaddr[(exports[idx]->seg)%MAX_SECTIONS_NUM];
        write_int32_le_file(fhndl,val);
      }
  }
  printf("Written %d function addresses into .DLL.\n",idx);
  fseek(fhndl,fnnames_table_raw_pos,SEEK_SET);
  for (idx=0;idx<num_of_exports;idx++)
  {
      if (exports[idx]==NULL)
      {
        write_int32_le_file(fhndl,0);
      } else
      {
        unsigned long val=exports[idx]->nmoffs;
        write_int32_le_file(fhndl,val);
      }
  }
  printf("Written %d function name offsets into .DLL.\n",idx);
  fclose(fhndl);
  long esection_used=end_of_used_space-sections_raddr[export_section_index];
  long esection_free=sections_rsize[export_section_index]-esection_used;
  printf("Used %d bytes in the export section; %d remain free.\n",esection_used,esection_free);
  sprintf(filename,"%s.def",module_name);
  fhndl=fopen(filename,"wb");
  if (fhndl==NULL)
  {
    printf("Can't open '%s' file!\n",filename);
    return 10;
  }
  fprintf(fhndl,"LIBRARY     %s.dll\n",module_name);
  fprintf(fhndl,"\nEXPORTS\n");
  for (idx=0;idx<num_of_exports;idx++)
  {
      if (exports[idx]==NULL)
        continue;
      char *name=exports[idx]->dstname;
      unsigned long val=exports[idx]->offs+sections_vaddr[(exports[idx]->seg)%MAX_SECTIONS_NUM];
      fprintf(fhndl,"    %-36s ; RVA=0x%08lX\n",name,val);
  }
  fclose(fhndl);
  printf("Written %d names into .DEF file.\n",idx);
  free(filename);
  return 0;
}


Back into main tutorial

This tutorial is written by Tomasz Lis.
It can be copied and changed, but a notification about original author should be included.