toolkit/crashreporter/LoadLibraryRemote.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/crashreporter/LoadLibraryRemote.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,438 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#ifndef __GNUC__
     1.9 +// disable warnings about pointer <-> DWORD conversions
    1.10 +#pragma warning( disable : 4311 4312 )
    1.11 +#endif
    1.12 +
    1.13 +#ifdef _WIN64
    1.14 +#define POINTER_TYPE ULONGLONG
    1.15 +#else
    1.16 +#define POINTER_TYPE DWORD
    1.17 +#endif
    1.18 +
    1.19 +#include <windows.h>
    1.20 +#include <winnt.h>
    1.21 +#include <stdlib.h>
    1.22 +#ifdef DEBUG_OUTPUT
    1.23 +#include <stdio.h>
    1.24 +#endif
    1.25 +
    1.26 +#include "nsWindowsHelpers.h"
    1.27 +
    1.28 +typedef const unsigned char* FileView;
    1.29 +
    1.30 +template<>
    1.31 +class nsAutoRefTraits<FileView>
    1.32 +{
    1.33 +public:
    1.34 +  typedef FileView RawRef;
    1.35 +  static FileView Void()
    1.36 +  {
    1.37 +    return nullptr;
    1.38 +  }
    1.39 +
    1.40 +  static void Release(RawRef aView)
    1.41 +  {
    1.42 +    if (nullptr != aView)
    1.43 +      UnmapViewOfFile(aView);
    1.44 +  }
    1.45 +};
    1.46 +
    1.47 +#ifndef IMAGE_SIZEOF_BASE_RELOCATION
    1.48 +// Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!?
    1.49 +#define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION))
    1.50 +#endif
    1.51 +
    1.52 +#include "LoadLibraryRemote.h"
    1.53 +
    1.54 +typedef struct {
    1.55 +  PIMAGE_NT_HEADERS headers;
    1.56 +  unsigned char *localCodeBase;
    1.57 +  unsigned char *remoteCodeBase;
    1.58 +  HMODULE *modules;
    1.59 +  int numModules;
    1.60 +} MEMORYMODULE, *PMEMORYMODULE;
    1.61 +
    1.62 +typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
    1.63 +
    1.64 +#define GET_HEADER_DICTIONARY(module, idx)  &(module)->headers->OptionalHeader.DataDirectory[idx]
    1.65 +
    1.66 +#ifdef DEBUG_OUTPUT
    1.67 +static void
    1.68 +OutputLastError(const char *msg)
    1.69 +{
    1.70 +  char* tmp;
    1.71 +  char *tmpmsg;
    1.72 +  FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    1.73 +                 nullptr, GetLastError(),
    1.74 +                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    1.75 +                 (LPSTR) &tmp, 0, nullptr);
    1.76 +  tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3);
    1.77 +  sprintf(tmpmsg, "%s: %s", msg, tmp);
    1.78 +  OutputDebugStringA(tmpmsg);
    1.79 +  LocalFree(tmpmsg);
    1.80 +  LocalFree(tmp);
    1.81 +}
    1.82 +#endif
    1.83 +
    1.84 +static void
    1.85 +CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module)
    1.86 +{
    1.87 +  int i;
    1.88 +  unsigned char *codeBase = module->localCodeBase;
    1.89 +  unsigned char *dest;
    1.90 +  PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
    1.91 +  for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
    1.92 +    dest = codeBase + section->VirtualAddress;
    1.93 +    memset(dest, 0, section->Misc.VirtualSize);
    1.94 +    if (section->SizeOfRawData) {
    1.95 +      memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData);
    1.96 +    }
    1.97 +    //     section->Misc.PhysicalAddress = (POINTER_TYPE) module->remoteCodeBase + section->VirtualAddress;
    1.98 +  }
    1.99 +}
   1.100 +
   1.101 +// Protection flags for memory pages (Executable, Readable, Writeable)
   1.102 +static int ProtectionFlags[2][2][2] = {
   1.103 +  {
   1.104 +    // not executable
   1.105 +    {PAGE_NOACCESS, PAGE_WRITECOPY},
   1.106 +    {PAGE_READONLY, PAGE_READWRITE},
   1.107 +  }, {
   1.108 +    // executable
   1.109 +    {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY},
   1.110 +    {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE},
   1.111 +  },
   1.112 +};
   1.113 +
   1.114 +static bool
   1.115 +FinalizeSections(PMEMORYMODULE module, HANDLE hRemoteProcess)
   1.116 +{
   1.117 +#ifdef DEBUG_OUTPUT
   1.118 +  fprintf(stderr, "Finalizing sections: local base %p, remote base %p\n",
   1.119 +          module->localCodeBase, module->remoteCodeBase);
   1.120 +#endif
   1.121 +
   1.122 +  int i;
   1.123 +  PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
   1.124 +  
   1.125 +  // loop through all sections and change access flags
   1.126 +  for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
   1.127 +    DWORD protect, oldProtect, size;
   1.128 +    int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
   1.129 +    int readable =   (section->Characteristics & IMAGE_SCN_MEM_READ) != 0;
   1.130 +    int writeable =  (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0;
   1.131 +
   1.132 +    // determine protection flags based on characteristics
   1.133 +    protect = ProtectionFlags[executable][readable][writeable];
   1.134 +    if (section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
   1.135 +      protect |= PAGE_NOCACHE;
   1.136 +    }
   1.137 +
   1.138 +    // determine size of region
   1.139 +    size = section->Misc.VirtualSize;
   1.140 +    if (size > 0) {
   1.141 +      void* remoteAddress = module->remoteCodeBase + section->VirtualAddress;
   1.142 +      void* localAddress = module->localCodeBase + section->VirtualAddress;
   1.143 +
   1.144 +#ifdef DEBUG_OUTPUT
   1.145 +      fprintf(stderr, "Copying section %s to %p, size %x, executable %i readable %i writeable %i\n",
   1.146 +              section->Name, remoteAddress, size, executable, readable, writeable);
   1.147 +#endif
   1.148 +
   1.149 +      // Copy the data from local->remote and set the memory protection
   1.150 +      if (!VirtualAllocEx(hRemoteProcess, remoteAddress, size, MEM_COMMIT, PAGE_READWRITE))
   1.151 +        return false;
   1.152 +
   1.153 +      if (!WriteProcessMemory(hRemoteProcess,
   1.154 +                              remoteAddress,
   1.155 +                              localAddress,
   1.156 +                              size,
   1.157 +                              nullptr)) {
   1.158 +#ifdef DEBUG_OUTPUT
   1.159 +        OutputLastError("Error writing remote memory.\n");
   1.160 +#endif
   1.161 +        return false;
   1.162 +      }
   1.163 +
   1.164 +      if (VirtualProtectEx(hRemoteProcess, remoteAddress, size, protect, &oldProtect) == 0) {
   1.165 +#ifdef DEBUG_OUTPUT
   1.166 +        OutputLastError("Error protecting memory page");
   1.167 +#endif
   1.168 +        return false;
   1.169 +      }
   1.170 +    }
   1.171 +  }
   1.172 +  return true;
   1.173 +}
   1.174 +
   1.175 +static void
   1.176 +PerformBaseRelocation(PMEMORYMODULE module, SIZE_T delta)
   1.177 +{
   1.178 +  DWORD i;
   1.179 +  unsigned char *codeBase = module->localCodeBase;
   1.180 +
   1.181 +  PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC);
   1.182 +  if (directory->Size > 0) {
   1.183 +    PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION) (codeBase + directory->VirtualAddress);
   1.184 +    for (; relocation->VirtualAddress > 0; ) {
   1.185 +      unsigned char *dest = codeBase + relocation->VirtualAddress;
   1.186 +      unsigned short *relInfo = (unsigned short *)((unsigned char *)relocation + IMAGE_SIZEOF_BASE_RELOCATION);
   1.187 +      for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) {
   1.188 +        DWORD *patchAddrHL;
   1.189 +#ifdef _WIN64
   1.190 +        ULONGLONG *patchAddr64;
   1.191 +#endif
   1.192 +        int type, offset;
   1.193 +
   1.194 +        // the upper 4 bits define the type of relocation
   1.195 +        type = *relInfo >> 12;
   1.196 +        // the lower 12 bits define the offset
   1.197 +        offset = *relInfo & 0xfff;
   1.198 +        
   1.199 +        switch (type)
   1.200 +        {
   1.201 +        case IMAGE_REL_BASED_ABSOLUTE:
   1.202 +          // skip relocation
   1.203 +          break;
   1.204 +
   1.205 +        case IMAGE_REL_BASED_HIGHLOW:
   1.206 +          // change complete 32 bit address
   1.207 +          patchAddrHL = (DWORD *) (dest + offset);
   1.208 +          *patchAddrHL += delta;
   1.209 +          break;
   1.210 +        
   1.211 +#ifdef _WIN64
   1.212 +        case IMAGE_REL_BASED_DIR64:
   1.213 +          patchAddr64 = (ULONGLONG *) (dest + offset);
   1.214 +          *patchAddr64 += delta;
   1.215 +          break;
   1.216 +#endif
   1.217 +
   1.218 +        default:
   1.219 +          //printf("Unknown relocation: %d\n", type);
   1.220 +          break;
   1.221 +        }
   1.222 +      }
   1.223 +
   1.224 +      // advance to next relocation block
   1.225 +      relocation = (PIMAGE_BASE_RELOCATION) (((char *) relocation) + relocation->SizeOfBlock);
   1.226 +    }
   1.227 +  }
   1.228 +}
   1.229 +
   1.230 +static int
   1.231 +BuildImportTable(PMEMORYMODULE module)
   1.232 +{
   1.233 +  int result=1;
   1.234 +  unsigned char *codeBase = module->localCodeBase;
   1.235 +
   1.236 +  PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT);
   1.237 +  if (directory->Size > 0) {
   1.238 +    PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress);
   1.239 +    PIMAGE_IMPORT_DESCRIPTOR importEnd = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress + directory->Size);
   1.240 +
   1.241 +    for (; importDesc < importEnd && importDesc->Name; importDesc++) {
   1.242 +      POINTER_TYPE *thunkRef;
   1.243 +      FARPROC *funcRef;
   1.244 +      HMODULE handle = GetModuleHandleA((LPCSTR) (codeBase + importDesc->Name));
   1.245 +      if (handle == nullptr) {
   1.246 +#if DEBUG_OUTPUT
   1.247 +        OutputLastError("Can't load library");
   1.248 +#endif
   1.249 +        result = 0;
   1.250 +        break;
   1.251 +      }
   1.252 +
   1.253 +      module->modules = (HMODULE *)realloc(module->modules, (module->numModules+1)*(sizeof(HMODULE)));
   1.254 +      if (module->modules == nullptr) {
   1.255 +        result = 0;
   1.256 +        break;
   1.257 +      }
   1.258 +
   1.259 +      module->modules[module->numModules++] = handle;
   1.260 +      if (importDesc->OriginalFirstThunk) {
   1.261 +        thunkRef = (POINTER_TYPE *) (codeBase + importDesc->OriginalFirstThunk);
   1.262 +        funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
   1.263 +      } else {
   1.264 +        // no hint table
   1.265 +        thunkRef = (POINTER_TYPE *) (codeBase + importDesc->FirstThunk);
   1.266 +        funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
   1.267 +      }
   1.268 +      for (; *thunkRef; thunkRef++, funcRef++) {
   1.269 +        if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) {
   1.270 +          *funcRef = (FARPROC)GetProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef));
   1.271 +        } else {
   1.272 +          PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef));
   1.273 +          *funcRef = (FARPROC)GetProcAddress(handle, (LPCSTR)&thunkData->Name);
   1.274 +        }
   1.275 +        if (*funcRef == 0) {
   1.276 +          result = 0;
   1.277 +          break;
   1.278 +        }
   1.279 +      }
   1.280 +
   1.281 +      if (!result) {
   1.282 +        break;
   1.283 +      }
   1.284 +    }
   1.285 +  }
   1.286 +
   1.287 +  return result;
   1.288 +}
   1.289 +
   1.290 +static void* MemoryGetProcAddress(PMEMORYMODULE module, const char *name);
   1.291 +
   1.292 +void* LoadRemoteLibraryAndGetAddress(HANDLE hRemoteProcess,
   1.293 +                                     const WCHAR* library,
   1.294 +                                     const char* symbol)
   1.295 +{
   1.296 +  // Map the DLL into memory
   1.297 +  nsAutoHandle hLibrary(
   1.298 +    CreateFile(library, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
   1.299 +               FILE_ATTRIBUTE_NORMAL, nullptr));
   1.300 +  if (INVALID_HANDLE_VALUE == hLibrary) {
   1.301 +#if DEBUG_OUTPUT
   1.302 +    OutputLastError("Couldn't CreateFile the library.\n");
   1.303 +#endif
   1.304 +    return nullptr;
   1.305 +  }
   1.306 +
   1.307 +  nsAutoHandle hMapping(
   1.308 +    CreateFileMapping(hLibrary, nullptr, PAGE_READONLY, 0, 0, nullptr));
   1.309 +  if (!hMapping) {
   1.310 +#if DEBUG_OUTPUT
   1.311 +    OutputLastError("Couldn't CreateFileMapping.\n");
   1.312 +#endif
   1.313 +    return nullptr;
   1.314 +  }
   1.315 +
   1.316 +  nsAutoRef<FileView> data(
   1.317 +    (const unsigned char*) MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0));
   1.318 +  if (!data) {
   1.319 +#if DEBUG_OUTPUT
   1.320 +    OutputLastError("Couldn't MapViewOfFile.\n");
   1.321 +#endif
   1.322 +    return nullptr;
   1.323 +  }
   1.324 +
   1.325 +  SIZE_T locationDelta;
   1.326 +
   1.327 +  PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)data.get();
   1.328 +  if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
   1.329 +#if DEBUG_OUTPUT
   1.330 +    OutputDebugStringA("Not a valid executable file.\n");
   1.331 +#endif
   1.332 +    return nullptr;
   1.333 +  }
   1.334 +
   1.335 +  PIMAGE_NT_HEADERS old_header = (PIMAGE_NT_HEADERS)(data + dos_header->e_lfanew);
   1.336 +  if (old_header->Signature != IMAGE_NT_SIGNATURE) {
   1.337 +#if DEBUG_OUTPUT
   1.338 +    OutputDebugStringA("No PE header found.\n");
   1.339 +#endif
   1.340 +    return nullptr;
   1.341 +  }
   1.342 +
   1.343 +  // reserve memory for image of library in this process and the target process
   1.344 +  unsigned char* localCode = (unsigned char*) VirtualAlloc(nullptr,
   1.345 +    old_header->OptionalHeader.SizeOfImage,
   1.346 +    MEM_RESERVE | MEM_COMMIT,
   1.347 +    PAGE_READWRITE);
   1.348 +  if (!localCode) {
   1.349 +#if DEBUG_OUTPUT
   1.350 +    OutputLastError("Can't reserve local memory.");
   1.351 +#endif
   1.352 +  }
   1.353 +
   1.354 +  unsigned char* remoteCode = (unsigned char*) VirtualAllocEx(hRemoteProcess, nullptr,
   1.355 +    old_header->OptionalHeader.SizeOfImage,
   1.356 +    MEM_RESERVE,
   1.357 +    PAGE_EXECUTE_READ);
   1.358 +  if (!remoteCode) {
   1.359 +#if DEBUG_OUTPUT
   1.360 +    OutputLastError("Can't reserve remote memory.");
   1.361 +#endif
   1.362 +  }
   1.363 +
   1.364 +  MEMORYMODULE result;
   1.365 +  result.localCodeBase = localCode;
   1.366 +  result.remoteCodeBase = remoteCode;
   1.367 +  result.numModules = 0;
   1.368 +  result.modules = nullptr;
   1.369 +
   1.370 +  // copy PE header to code
   1.371 +  memcpy(localCode, dos_header, dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders);
   1.372 +  result.headers = reinterpret_cast<PIMAGE_NT_HEADERS>(localCode + dos_header->e_lfanew);
   1.373 +
   1.374 +  // update position
   1.375 +  result.headers->OptionalHeader.ImageBase = (POINTER_TYPE)remoteCode;
   1.376 +
   1.377 +  // copy sections from DLL file block to new memory location
   1.378 +  CopySections(data, old_header, &result);
   1.379 +
   1.380 +  // adjust base address of imported data
   1.381 +  locationDelta = (SIZE_T)(remoteCode - old_header->OptionalHeader.ImageBase);
   1.382 +  if (locationDelta != 0) {
   1.383 +    PerformBaseRelocation(&result, locationDelta);
   1.384 +  }
   1.385 +
   1.386 +  // load required dlls and adjust function table of imports
   1.387 +  if (!BuildImportTable(&result)) {
   1.388 +    return nullptr;
   1.389 +  }
   1.390 +
   1.391 +  // mark memory pages depending on section headers and release
   1.392 +  // sections that are marked as "discardable"
   1.393 +  if (!FinalizeSections(&result, hRemoteProcess)) {
   1.394 +    return nullptr;
   1.395 +  }
   1.396 +
   1.397 +  return MemoryGetProcAddress(&result, symbol);
   1.398 +}
   1.399 +
   1.400 +static void* MemoryGetProcAddress(PMEMORYMODULE module, const char *name)
   1.401 +{
   1.402 +  unsigned char *localCodeBase = module->localCodeBase;
   1.403 +  int idx=-1;
   1.404 +  DWORD i, *nameRef;
   1.405 +  WORD *ordinal;
   1.406 +  PIMAGE_EXPORT_DIRECTORY exports;
   1.407 +  PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT);
   1.408 +  if (directory->Size == 0) {
   1.409 +    // no export table found
   1.410 +    return nullptr;
   1.411 +  }
   1.412 +
   1.413 +  exports = (PIMAGE_EXPORT_DIRECTORY) (localCodeBase + directory->VirtualAddress);
   1.414 +  if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) {
   1.415 +    // DLL doesn't export anything
   1.416 +    return nullptr;
   1.417 +  }
   1.418 +
   1.419 +  // search function name in list of exported names
   1.420 +  nameRef = (DWORD *) (localCodeBase + exports->AddressOfNames);
   1.421 +  ordinal = (WORD *) (localCodeBase + exports->AddressOfNameOrdinals);
   1.422 +  for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++) {
   1.423 +    if (stricmp(name, (const char *) (localCodeBase + (*nameRef))) == 0) {
   1.424 +      idx = *ordinal;
   1.425 +      break;
   1.426 +    }
   1.427 +  }
   1.428 +
   1.429 +  if (idx == -1) {
   1.430 +    // exported symbol not found
   1.431 +    return nullptr;
   1.432 +  }
   1.433 +
   1.434 +  if ((DWORD)idx > exports->NumberOfFunctions) {
   1.435 +    // name <-> ordinal number don't match
   1.436 +    return nullptr;
   1.437 +  }
   1.438 +
   1.439 +  // AddressOfFunctions contains the RVAs to the "real" functions
   1.440 +  return module->remoteCodeBase + (*(DWORD *) (localCodeBase + exports->AddressOfFunctions + (idx*4)));
   1.441 +}

mercurial