/*
    iwm-forwarding-win32
    Copyright (C) 2006 Kazuki IWAMOTO

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <shlwapi.h>


int
main (int   argc,
      char *argv[])
{
  char *fname;
  int i;
  FILE *fp;
  DWORD dwSignature;
  LPDWORD lpdwName;
  LPWORD lpwOrdinal;
  IMAGE_DOS_HEADER idh;
  IMAGE_FILE_HEADER ifh;
  IMAGE_OPTIONAL_HEADER ioh;
  IMAGE_EXPORT_DIRECTORY ied;
  PIMAGE_SECTION_HEADER pish;

  if (argc != 2)
    return -1;
  fname = malloc (lstrlenA (argv[1]) * sizeof (char));
  lstrcpyA (fname, argv[1]);
  PathStripPathA (fname);
  PathRemoveExtensionA (fname);
  fp = fopen (argv[1], "rb");
  if (!fp)
    return -1;
  if (fread (&idh, sizeof (IMAGE_DOS_HEADER), 1, fp) != 1
        || idh.e_magic != IMAGE_DOS_SIGNATURE
        || fseek (fp, idh.e_lfanew, SEEK_SET) != 0
        || fread (&dwSignature, sizeof (DWORD), 1, fp) != 1
        || dwSignature != IMAGE_NT_SIGNATURE
        || fread (&ifh, sizeof (IMAGE_FILE_HEADER), 1, fp) != 1
        || ifh.NumberOfSections == 0
        || fread (&ioh, sizeof (IMAGE_OPTIONAL_HEADER), 1, fp) != 1
        || ioh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0)
    {
      fclose (fp);
      return -1;
    }
  pish = malloc (ifh.NumberOfSections * sizeof (IMAGE_SECTION_HEADER));
  if (!pish || fread (pish, sizeof (IMAGE_SECTION_HEADER),
                            ifh.NumberOfSections, fp) != ifh.NumberOfSections)
    {
      fclose (fp);
      return -1;
    }
  for (i = 0; i < ifh.NumberOfSections; i++)
    if (pish[i].VirtualAddress
            <= ioh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
            && ioh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
                            < pish[i].VirtualAddress + pish[i].SizeOfRawData)
      {
        if (fseek (fp,
            ioh.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
            - pish[i].VirtualAddress + pish[i].PointerToRawData, SEEK_SET) != 0
                || fread (&ied, sizeof (IMAGE_EXPORT_DIRECTORY), 1, fp) != 1)
          {
            fclose (fp);
            return -1;
          }
        break;
      }
  lpdwName = malloc (ied.NumberOfNames * sizeof (DWORD));
  lpwOrdinal = malloc (ied.NumberOfNames * sizeof (WORD));
  for (i = 0; i < ifh.NumberOfSections; i++)
    if ((pish[i].VirtualAddress <= ied.AddressOfNames
        && ied.AddressOfNames < pish[i].VirtualAddress + pish[i].SizeOfRawData
        && (fseek (fp, ied.AddressOfNames
            - pish[i].VirtualAddress + pish[i].PointerToRawData, SEEK_SET) != 0
                    || fread (lpdwName, sizeof (DWORD), ied.NumberOfNames, fp)
                                                        != ied.NumberOfNames))
      || (pish[i].VirtualAddress <= ied.AddressOfNameOrdinals
        && ied.AddressOfNameOrdinals
                            < pish[i].VirtualAddress + pish[i].SizeOfRawData
        && (fseek (fp, ied.AddressOfNameOrdinals
            - pish[i].VirtualAddress + pish[i].PointerToRawData, SEEK_SET) != 0
                    || fread (lpwOrdinal, sizeof (WORD), ied.NumberOfNames, fp)
                                                        != ied.NumberOfNames)))
        {
          free (lpdwName);
          free (lpwOrdinal);
          fclose (fp);
          return -1;
        }
  puts ("EXPORTS");
  for (i = 0; i < ied.NumberOfNames; i++)
    {
      int j;

      for (j = 0; j < ifh.NumberOfSections; j++)
        if (pish[j].VirtualAddress <= lpdwName[i]
            && lpdwName[i] < pish[j].VirtualAddress + pish[j].SizeOfRawData)
          {
            char *name = NULL;
            int c, n = 0;

            if (fseek (fp, lpdwName[i] - pish[j].VirtualAddress
                                    + pish[j].PointerToRawData, SEEK_SET) != 0)
              {
                free (lpdwName);
                free (lpwOrdinal);
                fclose (fp);
                return -1;
              }
            while (c = fgetc (fp), c != EOF && c != '\0')
              {
                name = realloc (name, (n + 2) * sizeof (char));
                name[n++] = c;
              }
            if (name)
              name[n] = '\0';
            printf ("\t%s=%s.%s @%d\n",
                                name, fname, name, lpwOrdinal[i]+ ied.Base);
            free (name);
            break;
          }
    }
  free (lpdwName);
  free (lpwOrdinal);
  return fclose (fp);
}
