673 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			673 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* Copyright (c) 2009, 2010, 2011, 2013  Chris Faylor
 | |
| 
 | |
|   All rights reserved.
 | |
| 
 | |
|   Redistribution and use in source and binary forms, with or without
 | |
|   modification, are permitted provided that the following conditions are met:
 | |
| 
 | |
| 	* Redistributions of source code must retain the above copyright
 | |
| 	notice, this list of conditions and the following disclaimer.
 | |
| 	* Neither the name of the owner nor the names of its
 | |
| 	contributors may be used to endorse or promote products derived from
 | |
| 	this software without specific prior written permission.
 | |
| 
 | |
|   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
 | |
|   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 | |
|   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 | |
|   PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
 | |
|   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | |
|   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | |
|   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | |
|   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | |
|   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | |
|   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
| */
 | |
| 
 | |
| #include <errno.h>
 | |
| #include <getopt.h>
 | |
| #include <stdarg.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <wchar.h>
 | |
| #include <locale.h>
 | |
| #include <sys/cygwin.h>
 | |
| #include <cygwin/version.h>
 | |
| #include <unistd.h>
 | |
| #include <libgen.h>
 | |
| 
 | |
| #define _WIN32_WINNT 0x0501
 | |
| #include <windows.h>
 | |
| #include <imagehlp.h>
 | |
| #include <psapi.h>
 | |
| 
 | |
| #ifndef STATUS_DLL_NOT_FOUND
 | |
| #define STATUS_DLL_NOT_FOUND (0xC0000135L)
 | |
| #endif
 | |
| 
 | |
| struct option longopts[] =
 | |
| {
 | |
|   {"help", no_argument, NULL, 'h'},
 | |
|   {"verbose", no_argument, NULL, 'v'},
 | |
|   {"version", no_argument, NULL, 'V'},
 | |
|   {"data-relocs", no_argument, NULL, 'd'},
 | |
|   {"function-relocs", no_argument, NULL, 'r'},
 | |
|   {"unused", no_argument, NULL, 'u'},
 | |
|   {0, no_argument, NULL, 0}
 | |
| };
 | |
| const char *opts = "dhruvV";
 | |
| 
 | |
| static int process_file (const wchar_t *);
 | |
| 
 | |
| static int
 | |
| error (const char *fmt, ...)
 | |
| {
 | |
|   va_list ap;
 | |
|   va_start (ap, fmt);
 | |
|   fprintf (stderr, "ldd: ");
 | |
|   vfprintf (stderr, fmt, ap);
 | |
|   fprintf (stderr, "\nTry `ldd --help' for more information.\n");
 | |
|   exit (1);
 | |
| }
 | |
| 
 | |
| static void
 | |
| usage ()
 | |
| {
 | |
|   printf ("Usage: %s [OPTION]... FILE...\n\
 | |
| \n\
 | |
| Print shared library dependencies\n\
 | |
| \n\
 | |
|   -h, --help              print this help and exit\n\
 | |
|   -V, --version           print version information and exit\n\
 | |
|   -r, --function-relocs   process data and function relocations\n\
 | |
|                           (currently unimplemented)\n\
 | |
|   -u, --unused            print unused direct dependencies\n\
 | |
|                           (currently unimplemented)\n\
 | |
|   -v, --verbose           print all information\n\
 | |
|                           (currently unimplemented)\n",
 | |
| 	   program_invocation_short_name);
 | |
| }
 | |
| 
 | |
| static void
 | |
| print_version ()
 | |
| {
 | |
|   printf ("ldd (cygwin) %d.%d.%d\n"
 | |
| 	  "Print shared library dependencies\n"
 | |
| 	  "Copyright (C) 2009 - %s Chris Faylor\n"
 | |
| 	  "This is free software; see the source for copying conditions.  There is NO\n"
 | |
| 	  "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
 | |
| 	  CYGWIN_VERSION_DLL_MAJOR / 1000,
 | |
| 	  CYGWIN_VERSION_DLL_MAJOR % 1000,
 | |
| 	  CYGWIN_VERSION_DLL_MINOR,
 | |
| 	  strrchr (__DATE__, ' ') + 1);
 | |
| }
 | |
| 
 | |
| #define print_errno_error_and_return(__fn) \
 | |
|   do {\
 | |
|     fprintf (stderr, "ldd: %s: %s\n", (__fn), strerror (errno));\
 | |
|     return 1;\
 | |
|   } while (0)
 | |
| 
 | |
| #define set_errno_and_return(x) \
 | |
|   do {\
 | |
|     cygwin_internal (CW_SETERRNO, __FILE__, __LINE__ - 2);\
 | |
|     return (x);\
 | |
|   } while (0)
 | |
| 
 | |
| 
 | |
| static HANDLE hProcess;
 | |
| 
 | |
| static struct filelist
 | |
| {
 | |
|   struct filelist *next;
 | |
|   char *name;
 | |
| } *head;
 | |
| 
 | |
| static bool
 | |
| saw_file (char *name)
 | |
| {
 | |
|   filelist *p;
 | |
| 
 | |
|   for (p = head; p; p = p->next)
 | |
|     if (strcasecmp (name, p->name) == 0)
 | |
|       return true;
 | |
| 
 | |
|   p = (filelist *) malloc(sizeof (struct filelist));
 | |
|   p->next = head;
 | |
|   p->name = strdup (name);
 | |
|   head = p;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| static wchar_t *
 | |
| get_module_filename (HANDLE hp, HMODULE hm)
 | |
| {
 | |
|   size_t len;
 | |
|   wchar_t *buf = NULL;
 | |
|   DWORD res;
 | |
|   for (len = 1024; (res = GetModuleFileNameExW (hp, hm, (buf = (wchar_t *) realloc (buf, len * sizeof (wchar_t))), len)) == len; len += 1024)
 | |
|     continue;
 | |
|   if (!res)
 | |
|     {
 | |
|       free (buf);
 | |
|       buf = NULL;
 | |
|     }
 | |
|   return buf;
 | |
| }
 | |
| 
 | |
| static wchar_t *
 | |
| load_dll (const wchar_t *fn)
 | |
| {
 | |
|   wchar_t *buf = get_module_filename (GetCurrentProcess (), NULL);
 | |
|   if (!buf)
 | |
|     {
 | |
|       printf ("ldd: GetModuleFileName returned an error %u\n",
 | |
| 	      (unsigned int) GetLastError ());
 | |
|       exit (1);		/* FIXME */
 | |
|     }
 | |
| 
 | |
|   wchar_t *newbuf = (wchar_t *) malloc ((sizeof (L"\"\" -- ") + wcslen (buf) + wcslen (fn)) * sizeof (wchar_t));
 | |
|   newbuf[0] = L'"';
 | |
|   wcscpy (newbuf + 1, buf);
 | |
|   wchar_t *p = wcsstr (newbuf, L"\\ldd");
 | |
|   if (!p)
 | |
|     {
 | |
|       printf ("ldd: can't parse my own filename \"%ls\"\n", buf);
 | |
|       exit (1);
 | |
|     }
 | |
|   p[3] = L'h';
 | |
|   wcscat (newbuf, L"\" -- ");
 | |
|   wcscat (newbuf, fn);
 | |
|   free (buf);
 | |
|   return newbuf;
 | |
| }
 | |
| 
 | |
| static int
 | |
| start_process (const wchar_t *fn, bool& isdll)
 | |
| {
 | |
|   STARTUPINFOW si = {};
 | |
|   PROCESS_INFORMATION pi;
 | |
|   si.cb = sizeof (si);
 | |
|   wchar_t *cmd;
 | |
|   /* OCaml natdynlink plugins (.cmxs) cannot be handled by ldd because they
 | |
|      can only be loaded by flexdll_dlopen() */
 | |
|   if (wcslen (fn) < 4 || (wcscasecmp (wcschr (fn, L'\0') - 4, L".dll") != 0
 | |
|        && wcscasecmp (wcschr (fn, L'\0') - 4, L".oct") != 0
 | |
|        && wcscasecmp (wcschr (fn, L'\0') - 3, L".so") != 0))
 | |
|     {
 | |
|       cmd = wcsdup (fn);
 | |
|       isdll = false;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       cmd = load_dll (fn);
 | |
|       isdll = true;
 | |
|     }
 | |
|   if (CreateProcessW (NULL, cmd, NULL, NULL, FALSE, DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi))
 | |
|     {
 | |
|       free (cmd);
 | |
|       hProcess = pi.hProcess;
 | |
|       DebugSetProcessKillOnExit (true);
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   free (cmd);
 | |
|   set_errno_and_return (1);
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_entry_point_break ()
 | |
| {
 | |
|   HMODULE hm;
 | |
|   DWORD cbe;
 | |
|   SIZE_T cbw;
 | |
|   if (!EnumProcessModules (hProcess, &hm, sizeof (hm), &cbe) || !cbe)
 | |
|     set_errno_and_return (1);
 | |
| 
 | |
|   MODULEINFO mi = {};
 | |
|   if (!GetModuleInformation (hProcess, hm, &mi, sizeof (mi)) || !mi.EntryPoint)
 | |
|     set_errno_and_return (1);
 | |
| 
 | |
|   static const unsigned char int3 = 0xcc;
 | |
|   if (!WriteProcessMemory (hProcess, mi.EntryPoint, &int3, 1, &cbw) || cbw != 1)
 | |
|     set_errno_and_return (1);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| struct dlls
 | |
|   {
 | |
|     LPVOID lpBaseOfDll;
 | |
|     struct dlls *next;
 | |
|   };
 | |
| 
 | |
| #define SLOP strlen (" (?)")
 | |
| char *
 | |
| tocyg (wchar_t *win_fn)
 | |
| {
 | |
|   ssize_t cwlen = cygwin_conv_path (CCP_WIN_W_TO_POSIX, win_fn, NULL, 0);
 | |
|   char *fn;
 | |
|   if (cwlen <= 0)
 | |
|     {
 | |
|       int len = wcstombs (NULL, win_fn, 0) + 1;
 | |
|       if ((fn = (char *) malloc (len)))
 | |
| 	wcstombs (fn, win_fn, len);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       char *fn_cyg = (char *) malloc (cwlen + SLOP + 1);
 | |
|       if (cygwin_conv_path (CCP_WIN_W_TO_POSIX, win_fn, fn_cyg, cwlen) == 0)
 | |
| 	fn = fn_cyg;
 | |
|       else
 | |
| 	{
 | |
| 	  free (fn_cyg);
 | |
| 	  int len = wcstombs (NULL, win_fn, 0);
 | |
| 	  fn = (char *) malloc (len + SLOP + 1);
 | |
| 	  wcstombs (fn, win_fn, len + SLOP + 1);
 | |
| 	}
 | |
|     }
 | |
|   return fn;
 | |
| }
 | |
| 
 | |
| #define CYGWIN_DLL_LEN (wcslen (L"\\cygwin1.dll"))
 | |
| static int
 | |
| print_dlls (dlls *dll, const wchar_t *dllfn, const wchar_t *process_fn)
 | |
| {
 | |
|   head = NULL;			/* FIXME: memory leak */
 | |
|   while ((dll = dll->next))
 | |
|     {
 | |
|       char *fn;
 | |
|       wchar_t *fullpath = get_module_filename (hProcess, (HMODULE) dll->lpBaseOfDll);
 | |
|       if (!fullpath)
 | |
| 	fn = strdup ("???");
 | |
|       else if (dllfn && wcscmp (fullpath, dllfn) == 0)
 | |
| 	{
 | |
| 	  free (fullpath);
 | |
| 	  continue;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  fn = tocyg (fullpath);
 | |
| 	  saw_file (basename (fn));
 | |
| 	  free (fullpath);
 | |
| 	}
 | |
|       printf ("\t%s => %s (%p)\n", basename (fn), fn, dll->lpBaseOfDll);
 | |
|       free (fn);
 | |
|     }
 | |
|   if (process_fn)
 | |
|     return process_file (process_fn);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| report (const char *in_fn, bool multiple)
 | |
| {
 | |
|   if (multiple)
 | |
|     printf ("%s:\n", in_fn);
 | |
|   char *fn = realpath (in_fn, NULL);
 | |
|   if (!fn)
 | |
|     print_errno_error_and_return (in_fn);
 | |
| 
 | |
|   ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_W, fn, NULL, 0);
 | |
|   if (len <= 0)
 | |
|     print_errno_error_and_return (fn);
 | |
| 
 | |
|   bool isdll;
 | |
|   wchar_t fn_win[len + 1];
 | |
|   if (cygwin_conv_path (CCP_POSIX_TO_WIN_W, fn, fn_win, len))
 | |
|     print_errno_error_and_return (fn);
 | |
| 
 | |
|   if (!fn || start_process (fn_win, isdll))
 | |
|     print_errno_error_and_return (in_fn);
 | |
| 
 | |
|   DEBUG_EVENT ev;
 | |
| 
 | |
|   unsigned dll_count = 0;
 | |
| 
 | |
|   dlls dll_list = {};
 | |
|   dlls *dll_last = &dll_list;
 | |
|   const wchar_t *process_fn = NULL;
 | |
|   while (1)
 | |
|     {
 | |
|       bool exitnow = false;
 | |
|       DWORD cont = DBG_CONTINUE;
 | |
|       if (!WaitForDebugEvent (&ev, INFINITE))
 | |
| 	break;
 | |
|       switch (ev.dwDebugEventCode)
 | |
| 	{
 | |
| 	case LOAD_DLL_DEBUG_EVENT:
 | |
| 	  if (!isdll && ++dll_count == 2)
 | |
| 	    set_entry_point_break ();
 | |
| 	  dll_last->next = (dlls *) malloc (sizeof (dlls));
 | |
| 	  dll_last->next->lpBaseOfDll = ev.u.LoadDll.lpBaseOfDll;
 | |
| 	  dll_last->next->next = NULL;
 | |
| 	  dll_last = dll_last->next;
 | |
| 	  break;
 | |
| 	case EXCEPTION_DEBUG_EVENT:
 | |
| 	  switch (ev.u.Exception.ExceptionRecord.ExceptionCode)
 | |
| 	    {
 | |
| 	    case STATUS_DLL_NOT_FOUND:
 | |
| 	      process_fn = fn_win;
 | |
| 	      break;
 | |
| 	    case STATUS_BREAKPOINT:
 | |
| 	      if (!isdll)
 | |
| 		cont = DBG_EXCEPTION_NOT_HANDLED;
 | |
| 	      break;
 | |
| 	    }
 | |
| 	  break;
 | |
| 	case CREATE_THREAD_DEBUG_EVENT:
 | |
| 	  TerminateProcess (hProcess, 0);
 | |
| 	  break;
 | |
| 	case EXIT_PROCESS_DEBUG_EVENT:
 | |
| 	  print_dlls (&dll_list, isdll ? fn_win : NULL, process_fn);
 | |
| 	  exitnow = true;
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  break;
 | |
| 	}
 | |
|       if (!ContinueDebugEvent (ev.dwProcessId, ev.dwThreadId, cont))
 | |
| 	{
 | |
| 	  cygwin_internal (CW_SETERRNO, __FILE__, __LINE__ - 2);
 | |
| 	  print_errno_error_and_return (in_fn);
 | |
| 	}
 | |
|       if (exitnow)
 | |
| 	break;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| main (int argc, char **argv)
 | |
| {
 | |
|   int optch;
 | |
| 
 | |
|   /* Use locale from environment.  If not set or set to "C", use UTF-8. */
 | |
|   setlocale (LC_CTYPE, "");
 | |
|   if (!strcmp (setlocale (LC_CTYPE, NULL), "C"))
 | |
|     setlocale (LC_CTYPE, "en_US.UTF-8");
 | |
|   while ((optch = getopt_long (argc, argv, opts, longopts, NULL)) != -1)
 | |
|     switch (optch)
 | |
|       {
 | |
|       case 'd':
 | |
|       case 'r':
 | |
|       case 'u':
 | |
| 	error ("option not implemented `-%c'", optch);
 | |
| 	exit (1);
 | |
|       case 'h':
 | |
| 	usage ();
 | |
| 	exit (0);
 | |
|       case 'V':
 | |
| 	print_version ();
 | |
| 	return 0;
 | |
|       default:
 | |
| 	fprintf (stderr, "Try `%s --help' for more information.\n",
 | |
| 		 program_invocation_short_name);
 | |
| 	return 1;
 | |
|       }
 | |
|   argv += optind;
 | |
|   if (!*argv)
 | |
|     error ("missing file arguments");
 | |
| 
 | |
|   int ret = 0;
 | |
|   bool multiple = !!argv[1];
 | |
|   char *fn;
 | |
|   while ((fn = *argv++))
 | |
|     if (report (fn, multiple))
 | |
|       ret = 1;
 | |
|   exit (ret);
 | |
| }
 | |
| 
 | |
| static bool printing = false;
 | |
| 
 | |
| 
 | |
| /* dump of import directory
 | |
|    section begins at pointer 'section base'
 | |
|    section RVA is 'section_rva'
 | |
|    import directory begins at pointer 'imp' */
 | |
| static int
 | |
| dump_import_directory (const void *const section_base,
 | |
| 		       const DWORD section_rva,
 | |
| 		       const IMAGE_IMPORT_DESCRIPTOR *imp)
 | |
| {
 | |
|   /* get memory address given the RVA */
 | |
|   #define adr(rva) ((const void*) ((char*) section_base+((DWORD) (rva))-section_rva))
 | |
| 
 | |
|   /* continue until address inaccessible or there's no DLL name */
 | |
|   for (; !IsBadReadPtr (imp, sizeof (*imp)) && imp->Name; imp++)
 | |
|     {
 | |
|       wchar_t full_path[PATH_MAX];
 | |
|       wchar_t *dummy;
 | |
|       char *fn = (char *) adr (imp->Name);
 | |
| 
 | |
|       if (saw_file (fn))
 | |
| 	continue;
 | |
| 
 | |
|       int len = mbstowcs (NULL, fn, 0);
 | |
|       if (len <= 0)
 | |
| 	continue;
 | |
|       wchar_t fnw[len + 1];
 | |
|       mbstowcs (fnw, fn, len + 1);
 | |
|       /* output DLL's name */
 | |
|       char *print_fn;
 | |
|       if (!SearchPathW (NULL, fnw, NULL, PATH_MAX, full_path, &dummy))
 | |
| 	{
 | |
| 	  print_fn = strdup ("not found");
 | |
| 	  printing = true;
 | |
| 	}
 | |
|       else if (!printing)
 | |
| 	continue;
 | |
|       else
 | |
| 	{
 | |
| 	  print_fn = tocyg (full_path);
 | |
| 	  strcat (print_fn, " (?)");
 | |
| 	}
 | |
| 
 | |
|       printf ("\t%s => %s\n", (char *) fn, print_fn);
 | |
|       free (print_fn);
 | |
|     }
 | |
|   #undef adr
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* load a file in RAM (memory-mapped)
 | |
|    return pointer to loaded file
 | |
|    0 if no success  */
 | |
| static void *
 | |
| map_file (const wchar_t *filename)
 | |
| {
 | |
|   HANDLE hFile, hMapping;
 | |
|   void *basepointer;
 | |
|   if ((hFile = CreateFileW (filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
 | |
| 			   0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0)) == INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|       fprintf (stderr, "couldn't open %ls\n", filename);
 | |
|       return 0;
 | |
|     }
 | |
|   if (!(hMapping = CreateFileMapping (hFile, 0, PAGE_READONLY | SEC_COMMIT, 0, 0, 0)))
 | |
|     {
 | |
|       fprintf (stderr, "CreateFileMapping failed with windows error %u\n",
 | |
| 	       (unsigned int) GetLastError ());
 | |
|       CloseHandle (hFile);
 | |
|       return 0;
 | |
|     }
 | |
|   if (!(basepointer = MapViewOfFile (hMapping, FILE_MAP_READ, 0, 0, 0)))
 | |
|     {
 | |
|       fprintf (stderr, "MapViewOfFile failed with windows error %u\n",
 | |
| 	       (unsigned int) GetLastError ());
 | |
|       CloseHandle (hMapping);
 | |
|       CloseHandle (hFile);
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   CloseHandle (hMapping);
 | |
|   CloseHandle (hFile);
 | |
| 
 | |
|   return basepointer;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* this will return a pointer immediatly behind the DOS-header
 | |
|    0 if error */
 | |
| static void *
 | |
| skip_dos_stub (const IMAGE_DOS_HEADER *dos_ptr)
 | |
| {
 | |
|   /* look there's enough space for a DOS-header */
 | |
|   if (IsBadReadPtr (dos_ptr, sizeof (*dos_ptr)))
 | |
|       {
 | |
| 	fprintf (stderr, "not enough space for DOS-header\n");
 | |
| 	return 0;
 | |
|       }
 | |
| 
 | |
|    /* validate MZ */
 | |
|    if (dos_ptr->e_magic != IMAGE_DOS_SIGNATURE)
 | |
|       {
 | |
| 	fprintf (stderr, "not a DOS-stub\n");
 | |
| 	return 0;
 | |
|       }
 | |
| 
 | |
|   /* ok, then, go get it */
 | |
|   return (char*) dos_ptr + dos_ptr->e_lfanew;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* find the directory's section index given the RVA
 | |
|    Returns -1 if impossible */
 | |
| static int
 | |
| get_directory_index (const unsigned dir_rva,
 | |
| 		     const unsigned dir_length,
 | |
| 		     const int number_of_sections,
 | |
| 		     const IMAGE_SECTION_HEADER *sections)
 | |
| {
 | |
|   int sect;
 | |
|   for (sect = 0; sect < number_of_sections; sect++)
 | |
|   {
 | |
|     /* compare directory RVA to section RVA */
 | |
|     if (sections[sect].VirtualAddress <= dir_rva
 | |
|        && dir_rva < sections[sect].VirtualAddress+sections[sect].SizeOfRawData)
 | |
|       return sect;
 | |
|   }
 | |
| 
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| /* dump imports of a single file
 | |
|    Returns 0 if successful, !=0 else */
 | |
| static int
 | |
| process_file (const wchar_t *filename)
 | |
| {
 | |
|   void *basepointer;    /* Points to loaded PE file
 | |
| 			 * This is memory mapped stuff
 | |
| 			 */
 | |
|   int number_of_sections;
 | |
|   DWORD import_rva;           /* RVA of import directory */
 | |
|   DWORD import_length;        /* length of import directory */
 | |
|   int import_index;           /* index of section with import directory */
 | |
| 
 | |
|   /* ensure byte-alignment for struct tag_header */
 | |
|   #include <pshpack1.h>
 | |
| 
 | |
|   const struct tag_header
 | |
|     {
 | |
|       DWORD signature;
 | |
|       IMAGE_FILE_HEADER file_head;
 | |
|       IMAGE_OPTIONAL_HEADER opt_head;
 | |
|       IMAGE_SECTION_HEADER section_header[1];  /* an array of unknown length */
 | |
|     } *header;
 | |
| 
 | |
|   /* revert to regular alignment */
 | |
|   #include <poppack.h>
 | |
| 
 | |
|   printing = false;
 | |
| 
 | |
|   /* first, load file */
 | |
|   basepointer = map_file (filename);
 | |
|   if (!basepointer)
 | |
|       {
 | |
| 	puts ("cannot load file");
 | |
| 	return 1;
 | |
|       }
 | |
| 
 | |
|   /* get header pointer; validate a little bit */
 | |
|   header = (tag_header *) skip_dos_stub ((IMAGE_DOS_HEADER *) basepointer);
 | |
|   if (!header)
 | |
|       {
 | |
| 	puts ("cannot skip DOS stub");
 | |
| 	UnmapViewOfFile (basepointer);
 | |
| 	return 2;
 | |
|       }
 | |
| 
 | |
|   /* look there's enough space for PE headers */
 | |
|   if (IsBadReadPtr (header, sizeof (*header)))
 | |
|       {
 | |
| 	puts ("not enough space for PE headers");
 | |
| 	UnmapViewOfFile (basepointer);
 | |
| 	return 3;
 | |
|       }
 | |
| 
 | |
|   /* validate PE signature */
 | |
|   if (header->signature != IMAGE_NT_SIGNATURE)
 | |
|       {
 | |
| 	puts ("not a PE file");
 | |
| 	UnmapViewOfFile (basepointer);
 | |
| 	return 4;
 | |
|       }
 | |
| 
 | |
|   /* get number of sections */
 | |
|   number_of_sections = header->file_head.NumberOfSections;
 | |
| 
 | |
|   /* check there are sections... */
 | |
|   if (number_of_sections < 1)
 | |
|       {
 | |
| 	UnmapViewOfFile (basepointer);
 | |
| 	return 5;
 | |
|       }
 | |
| 
 | |
|   /* validate there's enough space for section headers */
 | |
|   if (IsBadReadPtr (header->section_header, number_of_sections*sizeof (IMAGE_SECTION_HEADER)))
 | |
|       {
 | |
| 	puts ("not enough space for section headers");
 | |
| 	UnmapViewOfFile (basepointer);
 | |
| 	return 6;
 | |
|       }
 | |
| 
 | |
|   /* get RVA and length of import directory */
 | |
|   import_rva = header->opt_head.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
 | |
|   import_length = header->opt_head.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
 | |
| 
 | |
|   /* check there's stuff to care about */
 | |
|   if (!import_rva || !import_length)
 | |
|       {
 | |
| 	UnmapViewOfFile (basepointer);
 | |
| 	return 0;       /* success! */
 | |
|     }
 | |
| 
 | |
|   /* get import directory pointer */
 | |
|   import_index = get_directory_index (import_rva,import_length,number_of_sections,header->section_header);
 | |
| 
 | |
|   /* check directory was found */
 | |
|   if (import_index < 0)
 | |
|       {
 | |
| 	puts ("couldn't find import directory in sections");
 | |
| 	UnmapViewOfFile (basepointer);
 | |
| 	return 7;
 | |
|       }
 | |
| 
 | |
|   /* The pointer to the start of the import directory's section */
 | |
|   const void *section_address = (char*) basepointer + header->section_header[import_index].PointerToRawData;
 | |
|   if (dump_import_directory (section_address,
 | |
| 			   header->section_header[import_index].VirtualAddress,
 | |
| 				    /* the last parameter is the pointer to the import directory:
 | |
| 				       section address + (import RVA - section RVA)
 | |
| 				       The difference is the offset of the import directory in the section */
 | |
| 			   (const IMAGE_IMPORT_DESCRIPTOR *) ((char *) section_address+import_rva-header->section_header[import_index].VirtualAddress)))
 | |
|     {
 | |
|       UnmapViewOfFile (basepointer);
 | |
|       return 8;
 | |
|     }
 | |
| 
 | |
|   UnmapViewOfFile (basepointer);
 | |
|   return 0;
 | |
| }
 |