blob: ce4ca223fdf19871431a5447b24f4cc780039580 [file] [log] [blame]
// Copyright 2005-2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
//
// Utility functions related to PE files (executables)
#include "omaha/base/pe_utils.h"
#include "omaha/base/debug.h"
#include "omaha/base/error.h"
#include "omaha/base/scoped_any.h"
namespace omaha {
namespace {
// Not really necessary as long as running on x86 architecture throughout, but
// what the hell
#if defined(BIG_ENDIAN)
uint32 GetUint32LE(void const * const p) {
uint8 const * const pu = reinterpret_cast<uint8 const * const>(p);
uint32 i = pu[0] | pu[1]<<8 | pu[2]<<16 | pu[3]<<24;
return i;
}
void PutUint32LE(uint32 i, void * const p) {
uint8 * const pu = reinterpret_cast<uint8 * const>(p);
pu[0] = i & 0xff;
pu[1] = (i >> 8) & 0xff;
pu[2] = (i >> 16) & 0xff;
pu[3] = (i >> 24) & 0xff;
}
#else // LITTLE_ENDIAN
inline uint32 GetUint32LE(void const * const p) {
uint32 const * const pu = reinterpret_cast<uint32 const * const>(p);
return *pu;
}
inline void PutUint32LE(uint32 i, void * const p) {
uint32 * const pu = reinterpret_cast<uint32 * const>(p);
*pu = i;
}
#endif
// Magic PE constants
const uint32 kPEHeaderOffset = 60;
const uint32 kPEHeaderChecksumOffset = 88;
const uint32 kPEHeaderSizeMin = 160;
const char magic_EXE_header[] = "MZ\0\0";
const char magic_PE_header[] = "PE\0\0";
} // namespace
HRESULT SetPEChecksum(const TCHAR *filename, uint32 checksum) {
// Write the checksum field of the Windows NT-specific "optional" header.
// Use Windows API calls rather than C library calls so that it will be
// really a small routine when used in the stub executable.
ASSERT(filename, (L""));
scoped_hfile file(::CreateFile(filename,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL));
if (!file)
return HRESULTFromLastError();
size_t size = ::GetFileSize(get(file), NULL);
if (size == INVALID_FILE_SIZE)
return HRESULTFromLastError();
scoped_file_mapping mapping(::CreateFileMapping(get(file),
NULL,
PAGE_READWRITE,
0,
0,
NULL));
if (!mapping)
return HRESULTFromLastError();
scoped_file_view file_data(::MapViewOfFile(get(mapping),
FILE_MAP_WRITE,
0,
0,
size));
if (!file_data)
return HRESULTFromLastError();
uint8 * image = reinterpret_cast<uint8 *>(get(file_data));
return SetPEChecksumToBuffer(image, size, checksum);
}
HRESULT GetPEChecksum(const TCHAR *filename, uint32 * checksum) {
// Read the checksum field out of the Windows NT-specific "optional" header.
// Use Windows API calls rather than C library calls so that it will be
// really a small routine when used in the stub executable.
ASSERT(filename, (L""));
ASSERT(checksum, (L""));
scoped_hfile file(::CreateFile(filename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL));
if (!file)
return HRESULTFromLastError();
size_t size = ::GetFileSize(get(file), NULL);
if (size == INVALID_FILE_SIZE)
return HRESULTFromLastError();
scoped_file_mapping mapping(::CreateFileMapping(get(file),
NULL,
PAGE_READONLY,
0,
0,
NULL));
if (!mapping)
return HRESULTFromLastError();
scoped_file_view file_data(::MapViewOfFile(get(mapping),
FILE_MAP_READ,
0,
0,
size));
if (!file_data)
return HRESULTFromLastError();
uint8 * image = reinterpret_cast<uint8 *>(get(file_data));
return GetPEChecksumFromBuffer(image, size, checksum);
}
HRESULT SetPEChecksumToBuffer(uint8 *buffer, size_t size, uint32 checksum) {
// Sanity checks
if (size < 64) {
ASSERT(false, (L"File too short to be valid executable"));
return E_FAIL;
}
uint32 x = GetUint32LE(magic_EXE_header);
if (::memcmp(buffer, &x, 2)) {
ASSERT(false, (L"Missing executable's magic number"));
return E_FAIL;
}
uint32 peheader = GetUint32LE(buffer + kPEHeaderOffset);
if (size < peheader + kPEHeaderSizeMin) {
ASSERT(false, (L"Too small given PE header size"));
return E_FAIL;
}
x = GetUint32LE(magic_PE_header);
if (::memcmp(buffer + peheader, &x, 4)) {
ASSERT(false, (L"Missing PE header magic number"));
return E_FAIL;
}
// Finally, write the checksum
PutUint32LE(checksum, &x);
::memcpy(buffer + peheader + kPEHeaderChecksumOffset, &x, 4);
return S_OK;
}
HRESULT GetPEChecksumFromBuffer(const unsigned char *buffer,
size_t size,
uint32 *checksum) {
// Sanity checks
if (size < 64) {
ASSERT(false, (L"File too short to be valid executable"));
return E_FAIL;
}
uint32 x = GetUint32LE(magic_EXE_header);
if (::memcmp(buffer, &x, 2)) {
ASSERT(false, (L"Missing executable's magic number"));
return E_FAIL;
}
uint32 peheader = GetUint32LE(buffer + kPEHeaderOffset);
if (size < peheader + kPEHeaderSizeMin) {
ASSERT(false, (L"Too small given PE header size"));
return E_FAIL;
}
x = GetUint32LE(magic_PE_header);
if (::memcmp(buffer + peheader, &x, 4)) {
ASSERT(false, (L"Missing PE header magic number"));
return E_FAIL;
}
// Finally, read the checksum
*checksum = GetUint32LE(buffer + peheader + kPEHeaderChecksumOffset);
return S_OK;
}
} // namespace omaha