├── .gitignore ├── PEDataSource.cpp ├── PEDataSource.h ├── PEDataTypes.h ├── PEFile.cpp ├── PEFile.h ├── PEFileResources.cpp ├── PEFileResources.h ├── PEVersion.cpp ├── PEVersion.h ├── README.md ├── build-mingw.bat ├── build-msvc-debug.bat ├── build-msvc.bat └── gpl.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | 9 | # Compiled Static libraries 10 | *.lib 11 | *.lai 12 | *.la 13 | *.a 14 | 15 | # Compiled Symbol files 16 | *.pdb -------------------------------------------------------------------------------- /PEDataSource.cpp: -------------------------------------------------------------------------------- 1 | // pe-file: library for reading and manipulating pe-files 2 | // Copyright (C) 2012 Jeffrey Bush jeff@coderforlife.com 3 | // 4 | // This library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | 18 | #ifdef __cplusplus_cli 19 | #pragma unmanaged 20 | #endif 21 | #include "PEDataSource.h" 22 | 23 | #include 24 | #include 25 | 26 | #ifdef USE_WINDOWS_API 27 | #ifdef ARRAYSIZE 28 | #undef ARRAYSIZE 29 | #endif 30 | #define WIN32_LEAN_AND_MEAN 31 | #include 32 | #else 33 | #include 34 | #include 35 | #endif 36 | 37 | using namespace PE; 38 | 39 | RawDataSource::RawDataSource(void* data, size_t size, bool readonly) : readonly(readonly), orig_data(data), sz(size) { 40 | if (readonly) { 41 | #ifdef USE_WINDOWS_API 42 | DWORD old_protect = 0; 43 | if ((this->d = (bytes)VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)) == NULL || 44 | memcpy(this->d, this->orig_data, size) == NULL || !VirtualProtect(this->d, size, PAGE_READONLY, &old_protect)) { this->close(); } 45 | #else 46 | if ((this->d = (bytes)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED || 47 | memcpy(this->d, this->orig_data, size) == NULL || mprotect(this->d, size, PROT_READ) == -1) { if (this->d == MAP_FAILED) { this->d = NULL; } this->close(); } 48 | #endif 49 | } else { 50 | this->d = this->orig_data; 51 | } 52 | } 53 | RawDataSource::~RawDataSource() { this->close(); } 54 | bool RawDataSource::isreadonly() const { return this->readonly; }; 55 | bool RawDataSource::flush() { return true; } 56 | void* RawDataSource::data() { return this->d; } 57 | size_t RawDataSource::size() const { return this->sz; } 58 | void RawDataSource::close() { 59 | if (this->d) { 60 | this->flush(); 61 | if (this->readonly) { 62 | #ifdef USE_WINDOWS_API 63 | VirtualFree(this->d, 0, MEM_RELEASE); 64 | #else 65 | munmap(this->d, this->sz); 66 | #endif 67 | } 68 | this->d = NULL; 69 | } 70 | if (this->orig_data) { free(this->orig_data); this->orig_data = NULL; } 71 | this->sz = 0; 72 | } 73 | bool RawDataSource::resize(size_t new_size) { 74 | if (this->readonly) { return false; } 75 | if (new_size == this->sz) { return true; } 76 | this->flush(); 77 | this->d = (bytes)realloc(this->orig_data, new_size); 78 | if (!this->d) { this->close(); return false; } 79 | this->orig_data = this->d; 80 | if (new_size < this->sz) 81 | memset((bytes)this->d+this->sz, 0, new_size-this->sz); // set new memory to 0 82 | this->sz = new_size; 83 | return true; 84 | } 85 | 86 | #pragma region Memory Map Management Functions 87 | /////////////////////////////////////////////////////////////////////////////// 88 | ///// Memory Map Management Functions 89 | /////////////////////////////////////////////////////////////////////////////// 90 | #include 91 | #include 92 | using namespace std; 93 | typedef map > MMFs; 94 | static MMFs mmfs; 95 | static void _RemoveMMF(MMFs &mmfs_, const_str file, void* x) { 96 | MMFs::iterator v = mmfs_.find(file); 97 | if (v != mmfs_.end()) { 98 | size_t size = v->second.size(); 99 | for (size_t i = 0; i < size; ++i) { 100 | if (v->second[i] == x) { 101 | if (size == 1) { 102 | mmfs_.erase(v); 103 | } else { 104 | if (i != size-1) // move the last element up 105 | v->second[i] = v->second[size-1]; 106 | v->second.pop_back(); // remove the last element 107 | } 108 | break; 109 | } 110 | } 111 | } 112 | } 113 | static void* AddMMF (const_str file, void* mm) { if (mm != NULL && mm != (void*)-1) { mmfs[file].push_back(mm); } return mm; } 114 | static void RemoveMMF(const_str file, void* mm) { _RemoveMMF(mmfs, file, mm); } 115 | 116 | #ifdef USE_WINDOWS_API 117 | static MMFs mmfViews; 118 | typedef BOOL (WINAPI *UNMAP_OR_CLOSE)(void*); 119 | static void* AddMMFView(const_str file, void* view) { if (view != NULL) mmfViews[file].push_back(view); return view; } 120 | static void RemoveMMFView(const_str file, void* view) { _RemoveMMF(mmfViews, file, view); } 121 | static void _UnmapAll(MMFs &mmfs_, const_str file, UNMAP_OR_CLOSE func) { 122 | MMFs::iterator v = mmfs_.find(file); 123 | if (v != mmfs_.end()) { 124 | size_t size = v->second.size(); 125 | for (size_t i = 0; i < size; ++i) 126 | func(v->second[i]); 127 | mmfs_.erase(v); 128 | } 129 | } 130 | void MemoryMappedDataSource::UnmapAllViewsOfFile(const_str file) { 131 | _UnmapAll(mmfs, file, &CloseHandle); 132 | _UnmapAll(mmfViews, file, (UNMAP_OR_CLOSE)&UnmapViewOfFile); 133 | } 134 | #else 135 | void MemoryMappedDataSource::UnmapAllViewsOfFile(const_str file) { 136 | MMFs::iterator v = mmfs.find(file); 137 | if (v != mmfs.end()) { 138 | size_t size = v->second.size(); 139 | for (size_t i = 0; i < size; ++i) 140 | munmap(v->second[i]); 141 | mmfs.erase(v); 142 | } 143 | } 144 | #endif 145 | #pragma endregion 146 | 147 | bool MemoryMappedDataSource::map() { 148 | #ifdef USE_WINDOWS_API 149 | return 150 | (this->hMap = AddMMF(this->original, CreateFileMapping(this->hFile, NULL, (readonly ? PAGE_READONLY : PAGE_READWRITE), 0, 0, NULL))) != NULL && 151 | (this->d = AddMMFView(this->original, MapViewOfFile(this->hMap, (readonly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS), 0, 0, 0))) != NULL; 152 | #else 153 | return (this->d = AddMMF(this->original, mmap(NULL, this->sz, (readonly ? PROT_READ : PROT_READ | PROT_WRITE), (readonly ? MAP_PRIVATE : MAP_SHARED), this->fd, 0))) != MAP_FAILED; 154 | #endif 155 | } 156 | void MemoryMappedDataSource::unmap() { 157 | #ifdef USE_WINDOWS_API 158 | if (this->hMap) { 159 | if (this->d) 160 | { 161 | this->flush(); 162 | UnmapViewOfFile(this->d); 163 | RemoveMMFView(this->original, this->d); 164 | this->d = NULL; 165 | } 166 | RemoveMMF(this->original, this->hMap); 167 | CloseHandle(this->hMap); 168 | this->hMap = NULL; 169 | } 170 | #else 171 | if (this->d == MAP_FAILED) { this->d = NULL; } 172 | else if (this->d) 173 | { 174 | this->flush(); 175 | munmap(this->d, this->sz); 176 | RemoveMMF(this->original, this->d); 177 | this->d = NULL; 178 | } 179 | #endif 180 | } 181 | #ifdef USE_WINDOWS_API 182 | MemoryMappedDataSource::MemoryMappedDataSource(const_str file, bool readonly) : readonly(readonly), hFile(INVALID_HANDLE_VALUE), hMap(NULL), d(NULL), sz(0) { 183 | #else 184 | MemoryMappedDataSource::MemoryMappedDataSource(const_str file, bool readonly) : readonly(readonly), fd(-1), d(NULL), sz(0) { 185 | #endif 186 | this->original[0] = 0; 187 | #ifdef USE_WINDOWS_API 188 | if (!GetFullPathName(file, ARRAYSIZE(this->original), this->original, NULL) || 189 | (this->hFile = CreateFile(this->original, (readonly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE)), FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE || 190 | (this->sz = GetFileSize(this->hFile, 0)) == INVALID_FILE_SIZE) 191 | { 192 | this->close(); 193 | } 194 | #else 195 | struct stat sb; 196 | if ((_wrealpath(file, this->original) == NULL) || 197 | (this->fd = _wopen(this->original, (readonly ? O_RDONLY : O_RDWR))) == -1 || 198 | fstat(this->fd, &sb) == -1) 199 | { 200 | this->close(); 201 | } 202 | this->sz = sb.st_size; 203 | #endif 204 | if (!map()) 205 | this->close(); 206 | } 207 | MemoryMappedDataSource::~MemoryMappedDataSource() { this->close(); } 208 | 209 | bool MemoryMappedDataSource::isreadonly() const { return this->readonly; }; 210 | bool MemoryMappedDataSource::flush() { 211 | #ifdef USE_WINDOWS_API 212 | return !this->readonly && FlushViewOfFile(this->d, 0) && FlushFileBuffers(this->hFile); 213 | #else 214 | return !this->readonly && msync(this->d, this->sz, MS_SYNC | MS_INVALIDATE) != -1; 215 | #endif 216 | } 217 | 218 | void* MemoryMappedDataSource::data() { return this->d; } 219 | size_t MemoryMappedDataSource::size() const { return this->sz; } 220 | void MemoryMappedDataSource::close() { 221 | this->unmap(); 222 | #ifdef USE_WINDOWS_API 223 | if (this->hFile != INVALID_HANDLE_VALUE) { CloseHandle(this->hFile); this->hFile = INVALID_HANDLE_VALUE; } 224 | #else 225 | if (this->fd != -1) { close(this->fd); this->fd = -1; } 226 | #endif 227 | this->sz = 0; 228 | } 229 | bool MemoryMappedDataSource::resize(size_t new_size) { 230 | if (this->readonly) { return false; } 231 | if (new_size == this->sz) { return true; } 232 | this->unmap(); 233 | #ifdef USE_WINDOWS_API 234 | if (SetFilePointer(this->hFile, (uint32_t)new_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER || !SetEndOfFile(this->hFile) || !this->map()) { this->close(); return false; } 235 | if (new_size > this->sz) 236 | memset((bytes)this->d+this->sz, 0, new_size-this->sz); // set new memory to 0 (I am unsure if Windows does this automatically like Linux does) 237 | #else 238 | if (ftruncate(this->fd, new_size) == -1 || !this->map()) { this->close(); return false; } 239 | #endif 240 | this->sz = new_size; 241 | return true; 242 | } 243 | -------------------------------------------------------------------------------- /PEDataSource.h: -------------------------------------------------------------------------------- 1 | // pe-file: library for reading and manipulating pe-files 2 | // Copyright (C) 2012 Jeffrey Bush jeff@coderforlife.com 3 | // 4 | // This library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | // Implements the classes and functions for reading and writing data 18 | 19 | #ifndef PE_DATA_SOURCE_H 20 | #define PE_DATA_SOURCE_H 21 | 22 | #include "PEDataTypes.h" 23 | 24 | namespace PE { 25 | class DataSourceImp { 26 | public: 27 | virtual bool isreadonly() const = 0; 28 | virtual void close() = 0; 29 | virtual bool flush() = 0; 30 | virtual void* data() = 0; 31 | virtual size_t size() const = 0; 32 | virtual bool resize(size_t new_size) = 0; 33 | }; 34 | 35 | class RawDataSource : public DataSourceImp { 36 | bool readonly; 37 | void *d, *orig_data; 38 | size_t sz; 39 | public: 40 | RawDataSource(void* data, size_t size, bool readonly = false); 41 | ~RawDataSource(); 42 | virtual bool isreadonly() const; 43 | virtual void* data(); 44 | virtual size_t size() const; 45 | virtual void close(); 46 | virtual bool resize(size_t new_size); 47 | virtual bool flush(); 48 | }; 49 | 50 | class MemoryMappedDataSource : public DataSourceImp { 51 | bool readonly; 52 | wchar_t original[LARGE_PATH]; 53 | #ifdef USE_WINDOWS_API 54 | void *hFile, *hMap; 55 | #else 56 | int fd; 57 | #endif 58 | void* d; 59 | size_t sz; 60 | 61 | bool map(); 62 | void unmap(); 63 | public: 64 | MemoryMappedDataSource(const_str file, bool readonly = false); 65 | ~MemoryMappedDataSource(); 66 | virtual bool isreadonly() const; 67 | virtual void* data(); 68 | virtual size_t size() const; 69 | virtual void close(); 70 | virtual bool resize(size_t new_size); 71 | virtual bool flush(); 72 | 73 | static void UnmapAllViewsOfFile(const_str file); 74 | }; 75 | 76 | class DataSource { 77 | DataSourceImp* ds; 78 | bool readonly; 79 | void* data; 80 | size_t sz; 81 | 82 | inline void update() { 83 | if (this->ds) { this->data = this->ds->data(); this->sz = this->ds->size(); } 84 | else { this->data = NULL; this->sz = 0; } 85 | } 86 | 87 | public: 88 | inline DataSource(DataSourceImp* ds) : ds(ds), readonly(ds && ds->isreadonly()), data(ds ? ds->data() : NULL), sz(ds ? ds->size() : 0) { } 89 | //inline static DataSource create(pntr data, size_t size, bool readonly = false) { return DataSource(new RawDataSource(data, size, readonly)); } 90 | //inline static DataSource create(const_str file, bool readonly = false) { return DataSource(new MemoryMappedDataSource(file, readonly)); } 91 | 92 | inline bool isopen() const { return this->data != NULL; } 93 | inline bool isreadonly() const { return this->readonly; } 94 | 95 | inline size_t size() const { return this->sz; } 96 | 97 | inline bool flush() { return this->ds->flush(); } 98 | inline void close() { if (this->ds) { this->ds->close(); delete this->ds; this->ds = NULL; this->update(); } } 99 | inline bool resize(size_t new_size) { bool retval = this->ds->resize(new_size); if (retval) { this->update(); } return retval; } 100 | 101 | //inline operator bool() const { return this->data != NULL; } // returns if the data is open 102 | 103 | //inline operator pntr() { return this->data; } 104 | //inline operator bytes() { return (bytes)this->data; } 105 | //inline operator const_pntr() const { return this->data; } 106 | //inline operator const_bytes() const { return (const_bytes)this->data; } 107 | 108 | inline dyn_ptr operator +(const size_t& off) { return dyn_ptr(&this->data, off); } 109 | inline const dyn_ptr operator +(const size_t& off) const { return dyn_ptr(&this->data, off); } 110 | 111 | inline ptrdiff_t operator -(const_bytes b) const { return (const_bytes)this->data - b; } 112 | 113 | inline byte& operator[](const size_t& off) { return ((bytes)this->data)[off]; } 114 | inline const byte& operator[](const size_t& off) const { return ((const_bytes)this->data)[off]; } 115 | }; 116 | 117 | inline ptrdiff_t operator -(const_bytes a, const DataSource& b) { return a - (b + 0); } 118 | } 119 | 120 | 121 | #endif -------------------------------------------------------------------------------- /PEDataTypes.h: -------------------------------------------------------------------------------- 1 | // pe-file: library for reading and manipulating pe-files 2 | // Copyright (C) 2012 Jeffrey Bush jeff@coderforlife.com 3 | // 4 | // This library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | // Implements the basic types, structures, functions, and constants used in many of the other files 18 | 19 | #ifndef PE_DATA_TYPES_H 20 | #define PE_DATA_TYPES_H 21 | 22 | #define _CRT_SECURE_NO_WARNINGS 23 | 24 | #ifdef _MSC_VER 25 | #pragma warning(disable : 4201 4480) // nonstandard extension used: [nameless struct/union | specifying underlying type for enum] 26 | #endif 27 | 28 | #ifdef _WIN32 29 | #define USE_WINDOWS_API 30 | #elif !defined(__posix) 31 | #error This code requires using either the Windows or POSIX APIs 32 | #endif 33 | 34 | #if defined(_MSC_VER) && _MSC_VER <= 1500 35 | typedef unsigned __int8 uint8_t; 36 | typedef unsigned __int16 uint16_t; 37 | typedef unsigned __int32 uint32_t; 38 | typedef unsigned __int64 uint64_t; 39 | typedef __int8 int8_t; 40 | typedef __int16 int16_t; 41 | typedef __int32 int32_t; 42 | typedef __int64 int64_t; 43 | #else 44 | #include 45 | #endif 46 | 47 | #include 48 | 49 | #define CASSERT(pred) switch(0){case 0:case pred:;} 50 | 51 | namespace PE { 52 | typedef uint8_t byte; 53 | typedef byte* bytes; 54 | typedef const byte* const_bytes; 55 | 56 | typedef wchar_t* str; 57 | typedef const wchar_t* const_str; 58 | 59 | typedef wchar_t* resid; 60 | typedef const wchar_t* const_resid; 61 | 62 | inline static bool IsIntResID(const_resid r) { return (((size_t)r) >> 16) == 0; } 63 | inline static resid MakeResID(uint16_t i) { return (resid)(size_t)i; } 64 | inline static uint16_t ResID2Int(const_resid r) { return (uint16_t)(size_t)r; } 65 | 66 | static const void*const null = NULL; 67 | 68 | // A class that acts as a pointer but automatically is updated when the underlying memory shifts 69 | template class dyn_ptr; 70 | template<> class dyn_ptr; 71 | template class dyn_ptr_base { 72 | template friend class dyn_ptr; 73 | 74 | protected: 75 | void*const* base; 76 | size_t off; 77 | inline T* get() { return ( T*)((*(const_bytes*)this->base)+this->off); } 78 | inline const T* get() const { return (const T*)((*(const_bytes*)this->base)+this->off); } 79 | 80 | template friend class dyn_ptr_base; 81 | 82 | inline dyn_ptr_base() : base((void*const*)&null), off(0) { } 83 | inline dyn_ptr_base(void*const* base, size_t off = 0) : base(base), off(off) { } 84 | inline dyn_ptr_base(void*const* base, T* val) : base(base), off((bytes)val - *(const_bytes*)base) { } 85 | 86 | public: 87 | inline operator bool () { return this->get() != NULL; } 88 | inline operator bool () const { return this->get() != NULL; } 89 | inline bool operator !() const { return this->get() == NULL; } 90 | inline operator T* () { return this->get(); } 91 | inline operator const T* () const { return this->get(); } 92 | 93 | inline bool equals(const dyn_ptr_base& b) const { return this->base == b.base && this->off == b.off; } 94 | 95 | inline bool operator ==(const dyn_ptr_base& b) const { return this->get() == b.get(); } 96 | inline bool operator !=(const dyn_ptr_base& b) const { return this->get() != b.get(); } 97 | inline bool operator <=(const dyn_ptr_base& b) const { return this->get() <= b.get(); } 98 | inline bool operator >=(const dyn_ptr_base& b) const { return this->get() >= b.get(); } 99 | inline bool operator < (const dyn_ptr_base& b) const { return this->get() < b.get(); } 100 | inline bool operator > (const dyn_ptr_base& b) const { return this->get() > b.get(); } 101 | inline bool operator ==(const T* b) const { return this->get() == b; } 102 | inline bool operator !=(const T* b) const { return this->get() != b; } 103 | inline bool operator <=(const T* b) const { return this->get() <= b; } 104 | inline bool operator >=(const T* b) const { return this->get() >= b; } 105 | inline bool operator < (const T* b) const { return this->get() < b; } 106 | inline bool operator > (const T* b) const { return this->get() > b; } 107 | friend inline bool operator ==(const T* a, const dyn_ptr_base& b) { return a == b.get(); } 108 | friend inline bool operator !=(const T* a, const dyn_ptr_base& b) { return a != b.get(); } 109 | friend inline bool operator <=(const T* a, const dyn_ptr_base& b) { return a <= b.get(); } 110 | friend inline bool operator >=(const T* a, const dyn_ptr_base& b) { return a >= b.get(); } 111 | friend inline bool operator < (const T* a, const dyn_ptr_base& b) { return a < b.get(); } 112 | friend inline bool operator > (const T* a, const dyn_ptr_base& b) { return a > b.get(); } 113 | //TODO: inline bool operator ==(int b) const { return this->get() == b; } 114 | //TODO: inline bool operator !=(int b) const { return this->get() == b; } 115 | //TODO: friend inline bool operator ==(int a, const dyn_ptr_base& b) { return a == b.get(); } 116 | //TODO: friend inline bool operator !=(int a, const dyn_ptr_base& b) { return a != b.get(); } 117 | }; 118 | template class dyn_ptr : public dyn_ptr_base { 119 | public: 120 | inline dyn_ptr() : dyn_ptr_base() { } 121 | inline dyn_ptr(void*const* base, size_t off_ = 0) : dyn_ptr_base(base, off_) { } 122 | inline dyn_ptr(void*const* base, T* val) : dyn_ptr_base(base, val) { } 123 | inline dyn_ptr(const dyn_ptr& b) : dyn_ptr_base(b.base, b.off) { } 124 | template inline dyn_ptr(const dyn_ptr_base& b) : dyn_ptr_base(b.base, b.off) { } 125 | template inline dyn_ptr(const dyn_ptr_base base, T* val) : dyn_ptr_base(base.base, val) { } 126 | inline dyn_ptr& operator =(const dyn_ptr& b) { this->base = b.base; this->off = b.off; return *this; } 127 | template inline dyn_ptr& operator =(const dyn_ptr_base& b) { this->base = b.base; this->off = b.off; return *this; } 128 | 129 | inline operator void*() { return this->get(); } 130 | inline operator const void*() const { return this->get(); } 131 | template inline operator dyn_ptr() { return dyn_ptr(this->base, this->off); } 132 | template inline operator const dyn_ptr() const { return dyn_ptr(this->base, this->off); } 133 | 134 | inline bool operator ==(const dyn_ptr_base& b) const { return this->get() == b.get(); } 135 | inline bool operator !=(const dyn_ptr_base& b) const { return this->get() != b.get(); } 136 | inline bool operator <=(const dyn_ptr_base& b) const { return this->get() <= b.get(); } 137 | inline bool operator >=(const dyn_ptr_base& b) const { return this->get() >= b.get(); } 138 | inline bool operator < (const dyn_ptr_base& b) const { return this->get() < b.get(); } 139 | inline bool operator > (const dyn_ptr_base& b) const { return this->get() > b.get(); } 140 | inline bool operator ==(const void* b) const { return this->get() == b; } 141 | inline bool operator !=(const void* b) const { return this->get() != b; } 142 | inline bool operator <=(const void* b) const { return this->get() <= b; } 143 | inline bool operator >=(const void* b) const { return this->get() >= b; } 144 | inline bool operator < (const void* b) const { return this->get() < b; } 145 | inline bool operator > (const void* b) const { return this->get() > b; } 146 | friend inline bool operator ==(const void* a, const dyn_ptr_base& b) { return a == b.get(); } 147 | friend inline bool operator !=(const void* a, const dyn_ptr_base& b) { return a != b.get(); } 148 | friend inline bool operator <=(const void* a, const dyn_ptr_base& b) { return a <= b.get(); } 149 | friend inline bool operator >=(const void* a, const dyn_ptr_base& b) { return a >= b.get(); } 150 | friend inline bool operator < (const void* a, const dyn_ptr_base& b) { return a < b.get(); } 151 | friend inline bool operator > (const void* a, const dyn_ptr_base& b) { return a > b.get(); } 152 | 153 | inline T& operator *() { return *this->get(); } 154 | inline const T& operator *() const { return *this->get(); } 155 | inline T* operator ->() { return this->get(); } 156 | inline const T* operator ->() const { return this->get(); } 157 | 158 | inline ptrdiff_t operator -(const dyn_ptr& b) const { return this->get() - b.get(); } 159 | inline ptrdiff_t operator -(const T* b) const { return this->get() - b; } 160 | inline ptrdiff_t operator -( T* b) const { return this->get() - b; } 161 | friend inline ptrdiff_t operator -(const T* a, const dyn_ptr& b) { return a - b.get(); } 162 | friend inline ptrdiff_t operator -( T* a, const dyn_ptr& b) { return a - b.get(); } 163 | 164 | inline dyn_ptr& operator ++() { this->off+=sizeof(T); return *this; } 165 | inline dyn_ptr& operator --() { this->off-=sizeof(T); return *this; } 166 | inline dyn_ptr operator ++(int) { dyn_ptr p = dyn_ptr(this->base, this->off); this->off+=sizeof(T); return p; } 167 | inline dyn_ptr operator --(int) { dyn_ptr p = dyn_ptr(this->base, this->off); this->off-=sizeof(T); return p; } 168 | 169 | // [] accessors for many integer sizes, both signed and unsigned 170 | #define ARRAY_ACCESSORS(I) \ 171 | inline T& operator [](const I& i) { return this->get()[i]; } \ 172 | inline const T& operator [](const I& i) const { return this->get()[i]; } 173 | ARRAY_ACCESSORS(size_t) 174 | ARRAY_ACCESSORS(ptrdiff_t) 175 | #if SIZE_MAX > 0xffffffffffffffff 176 | ARRAY_ACCESSORS(uint64_t) 177 | ARRAY_ACCESSORS(int64_t) 178 | #endif 179 | #if SIZE_MAX > 0xffffffff 180 | ARRAY_ACCESSORS(uint32_t) 181 | ARRAY_ACCESSORS(int32_t) 182 | #endif 183 | #if SIZE_MAX > 0xffff 184 | ARRAY_ACCESSORS(uint16_t) 185 | ARRAY_ACCESSORS(int16_t) 186 | #endif 187 | #if SIZE_MAX > 0xff 188 | ARRAY_ACCESSORS(uint8_t) 189 | ARRAY_ACCESSORS(int8_t) 190 | #endif 191 | #undef ARRAY_ACCESSORS 192 | 193 | // +/- operations for many integer sizes, both signed and unsigned 194 | #define PLUS_MINUS_OPS(I) \ 195 | inline dyn_ptr operator + (const I& off_) { return dyn_ptr(this->base, this->off + sizeof(T)*off_); } \ 196 | inline const dyn_ptr operator + (const I& off_) const { return dyn_ptr(this->base, this->off + sizeof(T)*off_); } \ 197 | inline dyn_ptr operator - (const I& off_) { return dyn_ptr(this->base, this->off - sizeof(T)*off_); } \ 198 | inline const dyn_ptr operator - (const I& off_) const { return dyn_ptr(this->base, this->off - sizeof(T)*off_); } \ 199 | inline dyn_ptr& operator +=(const I& off_) { this->off += sizeof(T)*off_; return *this; } \ 200 | inline dyn_ptr& operator -=(const I& off_) { this->off -= sizeof(T)*off_; return *this; } 201 | PLUS_MINUS_OPS(size_t) 202 | PLUS_MINUS_OPS(ptrdiff_t) 203 | #if SIZE_MAX > 0xffffffffffffffff 204 | PLUS_MINUS_OPS(uint64_t) 205 | PLUS_MINUS_OPS(int64_t) 206 | #endif 207 | #if SIZE_MAX > 0xffffffff 208 | PLUS_MINUS_OPS(uint32_t) 209 | PLUS_MINUS_OPS(int32_t) 210 | #endif 211 | #if SIZE_MAX > 0xffff 212 | PLUS_MINUS_OPS(uint16_t) 213 | PLUS_MINUS_OPS(int16_t) 214 | #endif 215 | #if SIZE_MAX > 0xff 216 | PLUS_MINUS_OPS(uint8_t) 217 | PLUS_MINUS_OPS(int8_t) 218 | #endif 219 | #undef PLUS_MINUS_OPS 220 | 221 | //TODO: are these possible? 222 | // ~ & | ^ << >> 223 | // &= |= ^= <<= >>= 224 | // ->* () , new new[] delete delete[] 225 | }; 226 | template<> class dyn_ptr : public dyn_ptr_base { 227 | public: 228 | inline dyn_ptr() : dyn_ptr_base() { } 229 | inline dyn_ptr(void*const* base, size_t off = 0) : dyn_ptr_base(base, off) { } 230 | inline dyn_ptr(void*const* base, void* val) : dyn_ptr_base(base, val) { } 231 | inline dyn_ptr(const dyn_ptr& b) : dyn_ptr_base(b.base, b.off) { } 232 | template inline dyn_ptr(const dyn_ptr_base& b) : dyn_ptr_base(b.base, b.off) { } 233 | template inline dyn_ptr(const dyn_ptr_base base, void* val) : dyn_ptr_base(base.base, val) { } 234 | inline dyn_ptr& operator =(const dyn_ptr& b) { this->base = b.base; this->off = b.off; return *this; } 235 | template inline dyn_ptr& operator =(const dyn_ptr_base& b) { this->base = b.base; this->off = b.off; return *this; } 236 | 237 | template inline operator dyn_ptr() { return dyn_ptr(this->base, this->off); } 238 | template inline operator const dyn_ptr() const { return dyn_ptr(this->base, this->off); } 239 | }; 240 | static const dyn_ptr nulldp; 241 | 242 | enum Overwrite { 243 | ALWAYS, // always adds the resource, even if it already exists 244 | NEVER, // only adds a resource is it does not already exist 245 | ONLY, // only adds a resource if it will overwrite another resource 246 | }; 247 | 248 | namespace Internal { 249 | #ifndef ARRAYSIZE 250 | #define ARRAYSIZE(a) sizeof(a)/sizeof(a[0]) 251 | #endif 252 | 253 | template inline static size_t roundUpTo(size_t x) { size_t mod = x % MULT; return (mod == 0) ? x : (x + MULT - mod); } 254 | template<> inline size_t roundUpTo<2>(size_t x) { return (x + 1) & ~0x1; } 255 | template<> inline size_t roundUpTo<4>(size_t x) { return (x + 3) & ~0x3; } 256 | inline static size_t roundUpTo(size_t x, size_t mult) { size_t mod = x % mult; return (mod == 0) ? x : (x + mult - mod); } 257 | } 258 | 259 | static const unsigned int LARGE_PATH = 32767; 260 | 261 | namespace Image { 262 | #include "pshpack2.h" 263 | struct DOSHeader { // IMAGE_DOS_HEADER - DOS .EXE header 264 | const static uint16_t SIGNATURE = 0x5A4D; // MZ 265 | 266 | uint16_t e_magic; // Magic number 267 | uint16_t e_cblp; // Bytes on last page of file 268 | uint16_t e_cp; // Pages in file 269 | uint16_t e_crlc; // Relocations 270 | uint16_t e_cparhdr; // Size of header in paragraphs 271 | uint16_t e_minalloc; // Minimum extra paragraphs needed 272 | uint16_t e_maxalloc; // Maximum extra paragraphs needed 273 | uint16_t e_ss; // Initial (relative) SS value 274 | uint16_t e_sp; // Initial SP value 275 | uint16_t e_csum; // Checksum 276 | uint16_t e_ip; // Initial IP value 277 | uint16_t e_cs; // Initial (relative) CS value 278 | uint16_t e_lfarlc; // File address of relocation table 279 | uint16_t e_ovno; // Overlay number 280 | uint16_t e_res[4]; // Reserved words 281 | uint16_t e_oemid; // OEM identifier (for e_oeminfo) 282 | uint16_t e_oeminfo; // OEM information; e_oemid specific 283 | uint16_t e_res2[10]; // Reserved words 284 | int32_t e_lfanew; // File address of new exe header 285 | }; 286 | #include "poppack.h" 287 | 288 | // 289 | // File header format. 290 | // 291 | struct FileHeader { // IMAGE_FILE_HEADER 292 | enum CharacteristicFlags : uint16_t { // IMAGE_FILE_* - FLAGS 293 | RELOCS_STRIPPED = 0x0001, // Relocation info stripped from file. 294 | EXECUTABLE_IMAGE = 0x0002, // File is executable (i.e. no unresolved external references). 295 | LINE_NUMS_STRIPPED = 0x0004, // Line numbers stripped from file. 296 | LOCAL_SYMS_STRIPPED = 0x0008, // Local symbols stripped from file. 297 | AGGRESIVE_WS_TRIM = 0x0010, // Aggressively trim working set 298 | LARGE_ADDRESS_AWARE = 0x0020, // App can handle >2gb addresses 299 | BYTES_REVERSED_LO = 0x0080, // Bytes of machine word are reversed. 300 | MACHINE_32BIT = 0x0100, // 32 bit word machine. 301 | DEBUG_STRIPPED = 0x0200, // Debugging info stripped from file in .DBG file 302 | REMOVABLE_RUN_FROM_SWAP = 0x0400, // If Image is on removable media, copy and run from the swap file. 303 | NET_RUN_FROM_SWAP = 0x0800, // If Image is on Net, copy and run from the swap file. 304 | SYSTEM = 0x1000, // System File. 305 | DLL = 0x2000, // File is a DLL. 306 | UP_SYSTEM_ONLY = 0x4000, // File should only be run on a UP machine 307 | BYTES_REVERSED_HI = 0x8000, // Bytes of machine word are reversed. 308 | }; 309 | enum MachineType : uint16_t { // IMAGE_FILE_MACHINE_* 310 | Unknown = 0, 311 | I386 = 0x014c, // Intel 386. 312 | R3000 = 0x0162, // MIPS little-endian, 0x160 big-endian 313 | R4000 = 0x0166, // MIPS little-endian 314 | R10000 = 0x0168, // MIPS little-endian 315 | WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2 316 | ALPHA = 0x0184, // Alpha_AXP 317 | SH3 = 0x01a2, // SH3 little-endian 318 | SH3DSP = 0x01a3, 319 | SH3E = 0x01a4, // SH3E little-endian 320 | SH4 = 0x01a6, // SH4 little-endian 321 | SH5 = 0x01a8, // SH5 322 | ARM = 0x01c0, // ARM Little-Endian 323 | THUMB = 0x01c2, 324 | AM33 = 0x01d3, 325 | POWERPC = 0x01F0, // IBM PowerPC Little-Endian 326 | POWERPCFP = 0x01f1, 327 | IA64 = 0x0200, // Intel 64 328 | MIPS16 = 0x0266, // MIPS 329 | ALPHA64 = 0x0284, // ALPHA64 330 | MIPSFPU = 0x0366, // MIPS 331 | MIPSFPU16 = 0x0466, // MIPS 332 | AXP64 = ALPHA64, 333 | TRICORE = 0x0520, // Infineon 334 | CEF = 0x0CEF, 335 | EBC = 0x0EBC, // EFI Byte Code 336 | AMD64 = 0x8664, // AMD64 (K8) 337 | M32R = 0x9041, // M32R little-endian 338 | CEE = 0xC0EE, 339 | }; 340 | 341 | MachineType Machine; 342 | uint16_t NumberOfSections; 343 | uint32_t TimeDateStamp; 344 | uint32_t PointerToSymbolTable; 345 | uint32_t NumberOfSymbols; 346 | uint16_t SizeOfOptionalHeader; 347 | CharacteristicFlags Characteristics; 348 | }; 349 | 350 | // 351 | // Directory format. 352 | // 353 | struct DataDirectory { // IMAGE_DATA_DIRECTORY 354 | static const int NUMBER_OF_ENTRIES = 16; 355 | enum DirectoryEntryType { // IMAGE_DIRECTORY_ENTRY_* 356 | EXPORT = 0, // Export Directory 357 | IMPORT = 1, // Import Directory 358 | RESOURCE = 2, // Resource Directory 359 | EXCEPTION = 3, // Exception Directory 360 | SECURITY = 4, // Security Directory 361 | BASERELOC = 5, // Base Relocation Table 362 | DEBUG = 6, // Debug Directory 363 | //COPYRIGHT = 7, // (X86 usage) 364 | ARCHITECTURE = 7, // Architecture Specific Data 365 | GLOBALPTR = 8, // RVA of GP 366 | TLS = 9, // TLS Directory 367 | LOAD_CONFIG = 10, // Load Configuration Directory 368 | BOUND_IMPORT = 11, // Bound Import Directory in headers 369 | IAT = 12, // Import Address Table 370 | DELAY_IMPORT = 13, // Delay Load Import Descriptors 371 | COM_DESCRIPTOR = 14, // COM Runtime descriptor 372 | }; 373 | 374 | uint32_t VirtualAddress; 375 | uint32_t Size; 376 | }; 377 | 378 | // 379 | // Optional header format. 380 | // 381 | struct OptionalHeader { // IMAGE_OPTIONAL_HEADER_* 382 | enum SubsystemType : uint16_t { // IMAGE_SUBSYSTEM_* 383 | UNKNOWN = 0, // Unknown subsystem. 384 | NATIVE = 1, // Image doesn't require a subsystem. 385 | WINDOWS_GUI = 2, // Image runs in the Windows GUI subsystem. 386 | WINDOWS_CUI = 3, // Image runs in the Windows character subsystem. 387 | OS2_CUI = 5, // image runs in the OS/2 character subsystem. 388 | POSIX_CUI = 7, // image runs in the Posix character subsystem. 389 | NATIVE_WINDOWS = 8, // image is a native Win9x driver. 390 | WINDOWS_CE_GUI = 9, // Image runs in the Windows CE subsystem. 391 | EFI_APPLICATION = 10, 392 | EFI_BOOT_SERVICE_DRIVER = 11, 393 | EFI_RUNTIME_DRIVER = 12, 394 | EFI_ROM = 13, 395 | XBOX = 14, 396 | WINDOWS_BOOT_APPLICATION = 16, 397 | }; 398 | enum DllCharacteristicFlags : uint16_t { // IMAGE_DLLCHARACTERISTICS_* - FLAGS 399 | //IMAGE_LIBRARY_PROCESS_INIT = 0x0001, // Reserved. 400 | //IMAGE_LIBRARY_PROCESS_TERM = 0x0002, // Reserved. 401 | //IMAGE_LIBRARY_THREAD_INIT = 0x0004, // Reserved. 402 | //IMAGE_LIBRARY_THREAD_TERM = 0x0008, // Reserved. 403 | DYNAMIC_BASE = 0x0040, // DLL can move. 404 | FORCE_INTEGRITY = 0x0080, // Code Integrity Image 405 | NX_COMPAT = 0x0100, // Image is NX compatible 406 | NO_ISOLATION = 0x0200, // Image understands isolation and doesn't want it 407 | NO_SEH = 0x0400, // Image does not use SEH. No SE handler may reside in this image 408 | NO_BIND = 0x0800, // Do not bind this image. 409 | //Reserved = 0x1000, 410 | WDM_DRIVER = 0x2000, // Driver uses WDM model 411 | //Reserved = 0x4000, 412 | TERMINAL_SERVER_AWARE = 0x8000, 413 | }; 414 | 415 | uint16_t Magic; 416 | byte MajorLinkerVersion; 417 | byte MinorLinkerVersion; 418 | uint32_t SizeOfCode; 419 | uint32_t SizeOfInitializedData; 420 | uint32_t SizeOfUninitializedData; 421 | uint32_t AddressOfEntryPoint; 422 | uint32_t BaseOfCode; 423 | union { 424 | struct 425 | { 426 | uint32_t BaseOfData; 427 | uint32_t ImageBase32; 428 | }; 429 | uint64_t ImageBase64; 430 | }; 431 | uint32_t SectionAlignment; 432 | uint32_t FileAlignment; 433 | uint16_t MajorOperatingSystemVersion; 434 | uint16_t MinorOperatingSystemVersion; 435 | uint16_t MajorImageVersion; 436 | uint16_t MinorImageVersion; 437 | uint16_t MajorSubsystemVersion; 438 | uint16_t MinorSubsystemVersion; 439 | uint32_t Win32VersionValue; 440 | uint32_t SizeOfImage; 441 | uint32_t SizeOfHeaders; 442 | uint32_t CheckSum; 443 | SubsystemType Subsystem; 444 | DllCharacteristicFlags DllCharacteristics; 445 | }; 446 | struct OptionalHeader32 : OptionalHeader { // IMAGE_OPTIONAL_HEADER 447 | static const uint16_t SIGNATURE = 0x10b; 448 | uint32_t SizeOfStackReserve; 449 | uint32_t SizeOfStackCommit; 450 | uint32_t SizeOfHeapReserve; 451 | uint32_t SizeOfHeapCommit; 452 | uint32_t LoaderFlags; 453 | uint32_t NumberOfRvaAndSizes; 454 | PE::Image::DataDirectory DataDirectory[PE::Image::DataDirectory::NUMBER_OF_ENTRIES]; 455 | }; 456 | struct OptionalHeader64 : OptionalHeader { // IMAGE_OPTIONAL_HEADER64 457 | static const uint16_t SIGNATURE = 0x20b; 458 | uint64_t SizeOfStackReserve; 459 | uint64_t SizeOfStackCommit; 460 | uint64_t SizeOfHeapReserve; 461 | uint64_t SizeOfHeapCommit; 462 | uint32_t LoaderFlags; 463 | uint32_t NumberOfRvaAndSizes; 464 | PE::Image::DataDirectory DataDirectory[PE::Image::DataDirectory::NUMBER_OF_ENTRIES]; 465 | }; 466 | 467 | struct NTHeaders { // IMAGE_NT_HEADERS_* 468 | static const uint32_t SIGNATURE = 0x00004550; // PE00 469 | uint32_t Signature; 470 | PE::Image::FileHeader FileHeader; 471 | }; 472 | struct NTHeaders64 : NTHeaders { // IMAGE_NT_HEADERS64 473 | OptionalHeader64 OptionalHeader; 474 | }; 475 | struct NTHeaders32 : NTHeaders { // IMAGE_NT_HEADERS 476 | OptionalHeader32 OptionalHeader; 477 | }; 478 | 479 | // 480 | // Section header format. 481 | // 482 | struct SectionHeader { // IMAGE_SECTION_HEADER 483 | enum CharacteristicFlags : uint32_t { // IMAGE_SCN_* - FLAGS 484 | //Reserved: TYPE_REG = 0x00000000, 485 | //Reserved: TYPE_DSECT = 0x00000001, 486 | //Reserved: TYPE_NOLOAD = 0x00000002, 487 | //Reserved: TYPE_GROUP = 0x00000004, 488 | TYPE_NO_PAD = 0x00000008, 489 | //Reserved: TYPE_COPY = 0x00000010, 490 | 491 | CNT_CODE = 0x00000020, // Section contains code. 492 | CNT_INITIALIZED_DATA = 0x00000040, // Section contains initialized data. 493 | CNT_UNINITIALIZED_DATA = 0x00000080, // Section contains uninitialized data. 494 | 495 | LNK_OTHER = 0x00000100, 496 | LNK_INFO = 0x00000200, // Section contains comments or some other type of information. 497 | //Reserved: TYPE_OVER = 0x00000400, 498 | LNK_REMOVE = 0x00000800, // Section contents will not become part of image. 499 | LNK_COMDAT = 0x00001000, // Section contents comdat. 500 | 501 | //Reserved: = 0x00002000, 502 | //Obsolete: MEM_PROTECTED = 0x00004000, 503 | NO_DEFER_SPEC_EXC = 0x00004000, // Reset speculative exceptions handling bits in the TLB entries for this section. 504 | GPREL = 0x00008000, // Section content can be accessed relative to GP 505 | MEM_FARDATA = 0x00008000, 506 | //Obsolete: MEM_SYSHEAP = 0x00010000, 507 | MEM_PURGEABLE = 0x00020000, 508 | MEM_16BIT = 0x00020000, 509 | MEM_LOCKED = 0x00040000, 510 | MEM_PRELOAD = 0x00080000, 511 | 512 | ALIGN_1BYTES = 0x00100000, 513 | ALIGN_2BYTES = 0x00200000, 514 | ALIGN_4BYTES = 0x00300000, 515 | ALIGN_8BYTES = 0x00400000, 516 | ALIGN_16BYTES = 0x00500000, // Default alignment if no others are specified. 517 | ALIGN_32BYTES = 0x00600000, 518 | ALIGN_64BYTES = 0x00700000, 519 | ALIGN_128BYTES = 0x00800000, 520 | ALIGN_256BYTES = 0x00900000, 521 | ALIGN_512BYTES = 0x00A00000, 522 | ALIGN_1024BYTES = 0x00B00000, 523 | ALIGN_2048BYTES = 0x00C00000, 524 | ALIGN_4096BYTES = 0x00D00000, 525 | ALIGN_8192BYTES = 0x00E00000, 526 | //Unused: = 0x00F00000, 527 | ALIGN_MASK = 0x00F00000, 528 | 529 | LNK_NRELOC_OVFL = 0x01000000, // Section contains extended relocations. 530 | MEM_DISCARDABLE = 0x02000000, // Section can be discarded. 531 | MEM_NOT_CACHED = 0x04000000, // Section is not cacheable. 532 | MEM_NOT_PAGED = 0x08000000, // Section is not pageable. 533 | MEM_SHARED = 0x10000000, // Section is shareable. 534 | MEM_EXECUTE = 0x20000000, // Section is executable. 535 | MEM_READ = 0x40000000, // Section is readable. 536 | MEM_WRITE = 0x80000000, // Section is writeable. 537 | 538 | TLS_SCALE_INDEX = 0x00000001, // Tls index is scaled 539 | }; 540 | 541 | byte Name[8]; 542 | uint32_t VirtualSize; // or PhysicalAddress 543 | uint32_t VirtualAddress; 544 | uint32_t SizeOfRawData; 545 | uint32_t PointerToRawData; 546 | uint32_t PointerToRelocations; 547 | uint32_t PointerToLinenumbers; 548 | uint16_t NumberOfRelocations; 549 | uint16_t NumberOfLinenumbers; 550 | CharacteristicFlags Characteristics; 551 | }; 552 | 553 | // 554 | // Based relocation format. 555 | // 556 | struct BaseRelocation { // IMAGE_BASE_RELOCATION 557 | enum RelBase : uint16_t { // IMAGE_REL_BASED_* 558 | ABSOLUTE = 0, 559 | HIGH = 1, 560 | LOW = 2, 561 | HIGHLOW = 3, 562 | HIGHADJ = 4, 563 | MIPS_JMPADDR = 5, 564 | MIPS_JMPADDR16 = 9, 565 | IA64_IMM64 = 9, 566 | DIR64 = 10, 567 | }; 568 | 569 | uint32_t VirtualAddress; 570 | uint32_t SizeOfBlock; 571 | // uint16_t TypeOffset[1]; 572 | }; 573 | 574 | // 575 | // Resource Format. 576 | // 577 | struct ResourceDirectory { // IMAGE_RESOURCE_DIRECTORY 578 | uint32_t Characteristics; 579 | uint32_t TimeDateStamp; 580 | uint16_t MajorVersion; 581 | uint16_t MinorVersion; 582 | uint16_t NumberOfNamedEntries; 583 | uint16_t NumberOfIdEntries; 584 | // IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[]; 585 | }; 586 | struct ResourceDirectoryEntry { // IMAGE_RESOURCE_DIRECTORY_ENTRY 587 | union { 588 | struct { 589 | uint32_t NameOffset:31; 590 | uint32_t NameIsString:1; 591 | }; 592 | uint32_t Name; 593 | uint16_t Id; 594 | }; 595 | union { 596 | uint32_t OffsetToData; 597 | struct { 598 | uint32_t OffsetToDirectory:31; 599 | uint32_t DataIsDirectory:1; 600 | }; 601 | }; 602 | }; 603 | //struct ResourceDirectoryString { // IMAGE_RESOURCE_DIRECTORY_STRING 604 | // uint16_t Length; 605 | // char NameString[1]; 606 | //}; 607 | //struct ResourceDirectoryStringU { // IMAGE_RESOURCE_DIR_STRING_U 608 | // uint16_t Length; 609 | // wchar_t NameString[1]; 610 | //}; 611 | struct ResourceDataEntry { // IMAGE_RESOURCE_DATA_ENTRY 612 | uint32_t OffsetToData; 613 | uint32_t Size; 614 | uint32_t CodePage; 615 | uint32_t Reserved; 616 | }; 617 | } 618 | 619 | namespace ResType { 620 | static const const_resid CURSOR = MakeResID(1); 621 | static const const_resid BITMAP = MakeResID(2); 622 | static const const_resid ICON = MakeResID(3); 623 | static const const_resid MENU = MakeResID(4); 624 | static const const_resid DIALOG = MakeResID(5); 625 | static const const_resid STRING = MakeResID(6); 626 | static const const_resid FONTDIR = MakeResID(7); 627 | static const const_resid FONT = MakeResID(8); 628 | static const const_resid ACCELERATOR = MakeResID(9); 629 | static const const_resid RCDATA = MakeResID(10); 630 | static const const_resid MESSAGETABLE = MakeResID(11); 631 | static const const_resid GROUP_CURSOR = MakeResID(12); 632 | static const const_resid GROUP_ICON = MakeResID(14); 633 | static const const_resid VERSION = MakeResID(16); 634 | static const const_resid DLGINCLUDE = MakeResID(17); 635 | static const const_resid PLUGPLAY = MakeResID(19); 636 | static const const_resid VXD = MakeResID(20); 637 | static const const_resid ANICURSOR = MakeResID(21); 638 | static const const_resid ANIICON = MakeResID(22); 639 | static const const_resid HTML = MakeResID(23); 640 | static const const_resid MANIFEST = MakeResID(24); 641 | } 642 | } 643 | 644 | #endif -------------------------------------------------------------------------------- /PEFile.cpp: -------------------------------------------------------------------------------- 1 | // pe-file: library for reading and manipulating pe-files 2 | // Copyright (C) 2012 Jeffrey Bush jeff@coderforlife.com 3 | // 4 | // This library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #ifdef __cplusplus_cli 18 | #pragma unmanaged 19 | #endif 20 | 21 | #define _DECLARE_ALL_PE_FILE_RESOURCES_ 22 | #include "PEFileResources.h" 23 | #include "PEFile.h" 24 | 25 | #ifdef USE_WINDOWS_API 26 | #ifdef ARRAYSIZE 27 | #undef ARRAYSIZE 28 | #endif 29 | #define WIN32_LEAN_AND_MEAN 30 | #include 31 | #undef ABSOLUTE 32 | #define set_err(e) SetLastError(e) 33 | #define get_err() GetLastError() 34 | #else 35 | #include 36 | #define ERROR_INVALID_DATA EFFORM 37 | #define set_err(e) errno = e 38 | #define get_err() errno 39 | #endif 40 | 41 | using namespace PE; 42 | using namespace PE::Image; 43 | using namespace PE::Internal; 44 | using namespace PE::Version; 45 | 46 | 47 | #pragma region GetResourceDirect and get just the offset of the resource 48 | /////////////////////////////////////////////////////////////////////////////// 49 | ///// GetResourceDirect - For performance reasons (and get just the offset of the resource) 50 | /////////////////////////////////////////////////////////////////////////////// 51 | #define FIRST_ENTRY ((resid)-1) 52 | inline static ResourceDirectory *GetDir(const ResourceDirectoryEntry *entry, const_bytes rsrc) { 53 | return (ResourceDirectory*)(rsrc + entry->OffsetToDirectory); 54 | } 55 | inline static ResourceDirectoryEntry *FirstEntry(const ResourceDirectory *dir) { 56 | return ((dir->NumberOfIdEntries + dir->NumberOfNamedEntries) < 1) ? NULL : (ResourceDirectoryEntry*)(dir+1); 57 | } 58 | inline static ResourceDirectory *FirstEntry(const ResourceDirectory *dir, const_bytes rsrc, const_resid *out = NULL) { 59 | const ResourceDirectoryEntry *entry = FirstEntry(dir); 60 | if (entry && out) 61 | *out = entry->NameIsString ? ((const_resid)(rsrc+entry->NameOffset+sizeof(uint16_t))) : MakeResID(entry->Id); 62 | return entry ? GetDir(entry, rsrc) : NULL; 63 | } 64 | inline static ResourceDirectory *FindEntryString(const ResourceDirectory *dir, const_resid id, const_bytes rsrc) { 65 | const ResourceDirectoryEntry *entries = (ResourceDirectoryEntry*)(dir+1); 66 | for (uint16_t i = 0; i < dir->NumberOfNamedEntries; i++) 67 | if (wcscmp(id, (resid)(rsrc+entries[i].NameOffset+sizeof(uint16_t))) == 0) 68 | return GetDir(entries+i, rsrc); 69 | return NULL; 70 | } 71 | inline static ResourceDirectory *FindEntryInt(const ResourceDirectory *dir, uint16_t id, const_bytes rsrc) { 72 | const ResourceDirectoryEntry *entries = (ResourceDirectoryEntry*)(dir+1)+dir->NumberOfNamedEntries; 73 | for (uint16_t i = 0; i < dir->NumberOfIdEntries; i++) 74 | if (entries[i].Id == id) 75 | return GetDir(entries+i, rsrc); 76 | return NULL; 77 | } 78 | inline static ResourceDirectory *FindEntry(const ResourceDirectory *dir, const_resid id, const_bytes rsrc, const_resid *out = NULL) { 79 | return (id == FIRST_ENTRY) ? FirstEntry(dir, rsrc, out) : (IsIntResID(id) ? FindEntryInt(dir, ResID2Int(id), rsrc) : FindEntryString(dir, id, rsrc)); 80 | } 81 | static void* GetResourceDirectInRsrc(bytes data, const SectionHeader *rsrcSect, const_resid type, const_resid name, const_resid *out_name = NULL, uint16_t *lang = NULL, size_t *size = NULL) { 82 | if (!rsrcSect || rsrcSect->PointerToRawData == 0 || rsrcSect->SizeOfRawData == 0) { return NULL; } 83 | 84 | // Get the bytes for the RSRC section 85 | bytes rsrc = data + rsrcSect->PointerToRawData; 86 | 87 | // Get the type and name directories 88 | const ResourceDirectory *dir = (ResourceDirectory*)rsrc; 89 | if ((dir = FindEntry(dir, type, rsrc, NULL)) == NULL) { return NULL; } 90 | if ((dir = FindEntry(dir, name, rsrc, out_name)) == NULL) { return NULL; } 91 | 92 | // Assume the first language 93 | const ResourceDirectoryEntry *entry; 94 | if ((entry = FirstEntry(dir)) == NULL || entry->DataIsDirectory) { return NULL; } 95 | const ResourceDataEntry *dataEntry = (ResourceDataEntry*)(rsrc+entry->OffsetToData); 96 | 97 | // Get the language and size of the resource 98 | if (lang) *lang = entry->Id; 99 | if (size) *size = dataEntry->Size; 100 | 101 | // Return the size and data 102 | return rsrc+dataEntry->OffsetToData-rsrcSect->VirtualAddress; 103 | } 104 | void* File::GetResourceDirect(void* _data, const_resid type, const_resid name) { 105 | bytes data = (bytes)_data; 106 | 107 | // Load and check headers 108 | const DOSHeader *dosh = (DOSHeader*)data; 109 | if (dosh->e_magic != DOSHeader::SIGNATURE) { return NULL; } 110 | int32_t peOffset = dosh->e_lfanew; 111 | const NTHeaders32 *nth = (NTHeaders32*)(data+peOffset); 112 | if (nth->Signature != NTHeaders::SIGNATURE) { return NULL; } 113 | const FileHeader* header = &nth->FileHeader; // identical for 32 and 64 bits 114 | const OptionalHeader* opt = &nth->OptionalHeader; 115 | bool is64bit = !(header->Characteristics & FileHeader::MACHINE_32BIT); 116 | if ((is64bit && opt->Magic != OptionalHeader64::SIGNATURE) || (!is64bit && opt->Magic != OptionalHeader32::SIGNATURE)) { return NULL; } 117 | 118 | // Get the RSRC section 119 | const SectionHeader *sections = (SectionHeader*)(data+peOffset+sizeof(uint32_t)+sizeof(FileHeader)+header->SizeOfOptionalHeader); 120 | const SectionHeader *rsrcSect = NULL; 121 | for (uint16_t i = 0; i < header->NumberOfSections; ++i) 122 | if (strncmp((const char*)sections[i].Name, ".rsrc", ARRAYSIZE(sections[i].Name)) == 0) { rsrcSect = sections+i; break; } 123 | 124 | // Get the resource within the RSRC section 125 | return GetResourceDirectInRsrc(data, rsrcSect, type, name); 126 | } 127 | #pragma endregion 128 | 129 | #pragma region Loading Functions 130 | /////////////////////////////////////////////////////////////////////////////// 131 | ///// Loading Functions 132 | /////////////////////////////////////////////////////////////////////////////// 133 | File::File(void* data, size_t size, bool readonly) : data(new RawDataSource(data, size, readonly)), res(NULL), modified(false) { 134 | if (!this->data.isopen() || !this->load()) { this->unload(); } 135 | } 136 | File::File(const_str file, bool readonly) : data(new MemoryMappedDataSource(file, readonly)), res(NULL), modified(false) { 137 | if (!this->data.isopen() || !this->load()) { this->unload(); } 138 | } 139 | File::File(DataSource data) : data(data), res(NULL), modified(false) { 140 | if (!this->data.isopen() || !this->load()) { this->unload(); } 141 | } 142 | bool File::load() { 143 | this->dosh = (dyn_ptr)(this->data + 0); 144 | if (this->dosh->e_magic != DOSHeader::SIGNATURE) { set_err(ERROR_INVALID_DATA); return false; } 145 | this->peOffset = this->dosh->e_lfanew; 146 | 147 | this->nth32 = (dyn_ptr)(this->data + this->peOffset); 148 | this->nth64 = (dyn_ptr)(this->data + this->peOffset); 149 | if (this->nth32->Signature != NTHeaders::SIGNATURE) { set_err(ERROR_INVALID_DATA); return false; } 150 | this->header = dyn_ptr(this->dosh, &this->nth32->FileHeader); // identical for 32 and 64 bits 151 | this->opt = dyn_ptr(this->dosh, &this->nth32->OptionalHeader); // beginning is identical for 32 and 64 bits 152 | bool is64bit = this->is64bit(), is32bit = this->is32bit(); 153 | 154 | if ((is64bit && this->opt->Magic != OptionalHeader64::SIGNATURE) || 155 | (is32bit && this->opt->Magic != OptionalHeader32::SIGNATURE) || 156 | (is64bit == is32bit)) { set_err(ERROR_INVALID_DATA); return false; } 157 | 158 | this->dataDir = dyn_ptr(this->dosh, is64bit ? this->nth64->OptionalHeader.DataDirectory : this->nth32->OptionalHeader.DataDirectory); 159 | this->sections = (dyn_ptr)(this->data+this->peOffset+sizeof(uint32_t)+sizeof(FileHeader)+this->header->SizeOfOptionalHeader); 160 | 161 | // Load resources 162 | dyn_ptr rsrc = this->getSectionHeader(".rsrc"); 163 | 164 | // Create resources object 165 | if ((this->res = Rsrc::createFromRSRCSection(this->data+0, this->data.size(), rsrc)) == NULL) 166 | return false; 167 | 168 | // Get the current version and modification information from the resources 169 | FileVersionBasicInfo *v = FileVersionBasicInfo::Get(GetResourceDirectInRsrc(this->data+0, rsrc, ResType::VERSION, FIRST_ENTRY)); 170 | if (v) { 171 | this->version = v->FileVersion; 172 | this->modified = (v->FileFlagsMask & v->FileFlags & (FileVersionBasicInfo::PATCHED | FileVersionBasicInfo::SPECIALBUILD)) > 0; 173 | } 174 | 175 | return true; 176 | } 177 | File::~File() { unload(); } 178 | void File::unload() { 179 | uint32_t err = get_err(); 180 | if (this->res) { delete this->res; this->res = NULL; } 181 | if (this->data.isopen()) { this->data.close(); } 182 | this->sections = nulldp; 183 | set_err(err); 184 | } 185 | bool File::isLoaded() const { return this->data.isopen(); } 186 | bool File::isReadOnly() const { return this->data.isreadonly(); } 187 | #pragma endregion 188 | 189 | #pragma region Header Functions 190 | /////////////////////////////////////////////////////////////////////////////// 191 | ///// Header Functions 192 | /////////////////////////////////////////////////////////////////////////////// 193 | bool File::is32bit() const { return (this->header->Characteristics & FileHeader::MACHINE_32BIT) != 0; } 194 | bool File::is64bit() const { return (this->header->Characteristics & FileHeader::MACHINE_32BIT) == 0; } 195 | uint64_t File::getImageBase() const { return this->is64bit() ? this->opt->ImageBase64 : this->opt->ImageBase32; } 196 | 197 | dyn_ptr File::getFileHeader() { return this->header; } 198 | dyn_ptr File::getNtHeaders32() { return this->nth32; } 199 | dyn_ptr File::getNtHeaders64() { return this->nth64; } 200 | 201 | const dyn_ptr File::getFileHeader() const { return this->header; } 202 | const dyn_ptr File::getNtHeaders32() const { return this->nth32; } 203 | const dyn_ptr File::getNtHeaders64() const { return this->nth64; } 204 | 205 | uint32_t File::getDataDirectoryCount() const { return this->is64bit() ? this->nth64->OptionalHeader.NumberOfRvaAndSizes : this->nth32->OptionalHeader.NumberOfRvaAndSizes; } 206 | dyn_ptr File::getDataDirectory(int i) { return this->dataDir+i; } 207 | const dyn_ptr File::getDataDirectory(int i) const { return this->dataDir+i; } 208 | 209 | int File::getSectionHeaderCount() const { return this->header->NumberOfSections; } 210 | dyn_ptr File::getSectionHeader(int i) { return this->sections+i; } 211 | dyn_ptr File::getSectionHeader(const char *str, int *index) { 212 | for (uint16_t i = 0; i < this->header->NumberOfSections; i++) { 213 | if (strncmp((const char*)this->sections[i].Name, str, ARRAYSIZE(this->sections[i].Name)) == 0) { 214 | if (index) *index = i; 215 | return this->sections+i; 216 | } 217 | } 218 | return nulldp; 219 | } 220 | dyn_ptr File::getSectionHeaderByRVA(uint32_t rva, int *index) { 221 | for (uint16_t i = 0; i < this->header->NumberOfSections; i++) 222 | if (this->sections[i].VirtualAddress <= rva && rva < this->sections[i].VirtualAddress + this->sections[i].VirtualSize) { 223 | if (index) *index = i; 224 | return this->sections+i; 225 | } 226 | return nulldp; 227 | } 228 | dyn_ptr File::getSectionHeaderByVA(uint64_t va, int *index) { return this->getSectionHeaderByRVA((uint32_t)(va - this->getImageBase()), index); } 229 | const dyn_ptr File::getSectionHeader(int i) const { return this->sections+i; } 230 | const dyn_ptr File::getSectionHeader(const char *str, int *index) const { 231 | for (uint16_t i = 0; i < this->header->NumberOfSections; i++) { 232 | if (strncmp((const char*)this->sections[i].Name, str, ARRAYSIZE(this->sections[i].Name)) == 0) { 233 | if (index) *index = i; 234 | return this->sections+i; 235 | } 236 | } 237 | return nulldp; 238 | } 239 | const dyn_ptr File::getSectionHeaderByRVA(uint32_t rva, int *index) const { 240 | for (uint16_t i = 0; i < this->header->NumberOfSections; i++) 241 | if (this->sections[i].VirtualAddress <= rva && rva < this->sections[i].VirtualAddress + this->sections[i].VirtualSize) { 242 | if (index) *index = i; 243 | return this->sections+i; 244 | } 245 | return nulldp; 246 | } 247 | const dyn_ptr File::getSectionHeaderByVA(uint64_t va, int *index) const { return this->getSectionHeaderByRVA((uint32_t)(va - this->getImageBase()), index); } 248 | #pragma endregion 249 | 250 | #pragma region Special Section Header Functions 251 | /////////////////////////////////////////////////////////////////////////////// 252 | ///// Special Section Header Functions 253 | /////////////////////////////////////////////////////////////////////////////// 254 | dyn_ptr File::getExpandedSectionHdr(int i, uint32_t room) { 255 | if (i >= this->header->NumberOfSections) { return nulldp; } 256 | 257 | dyn_ptr sect = this->sections+i; 258 | uint32_t size = sect->SizeOfRawData, vs = sect->VirtualSize, min_size = vs + room; 259 | 260 | // Check if expansion is necessary 261 | if (min_size <= size) { return sect; } 262 | 263 | // Get the file and section alignment values 264 | uint32_t salign = this->opt->SectionAlignment, falign = this->opt->FileAlignment; 265 | 266 | // Check if expansion is possible 267 | if (roundUpTo(vs, salign) < min_size) { return nulldp; } 268 | 269 | // Move by a multiple of "file alignment" 270 | uint32_t new_size = (uint32_t)roundUpTo(min_size, falign), move = new_size - size; 271 | 272 | // Increase file size (invalidates all local pointers to the file data) 273 | if (!this->setSize(this->data.size()+move)) { return nulldp; } 274 | sect = this->sections+i; // update the section header pointer 275 | 276 | // Shift data and fill space with zeros 277 | uint32_t end = sect->PointerToRawData + size; 278 | if (!this->shift(end, move) || !this->zero(move, end)) { return nulldp; } 279 | 280 | // Update section headers 281 | sect->SizeOfRawData += move; // update the size of the expanding section header 282 | for (uint16_t s = (uint16_t)i + 1; s < this->header->NumberOfSections; ++s) // update the location of all subsequent sections 283 | this->sections[s].PointerToRawData += move; 284 | dyn_ptr dir; // update the certificate entry if it exists 285 | if (this->getDataDirectoryCount() > DataDirectory::SECURITY && (dir = this->dataDir+DataDirectory::SECURITY) != nulldp && dir->VirtualAddress && dir->Size) 286 | dir->VirtualAddress += move; 287 | 288 | // Update NT header with new size information 289 | uint32_t chars = sect->Characteristics; 290 | this->opt->SizeOfImage += move; 291 | if (chars & SectionHeader::CNT_CODE) this->opt->SizeOfCode += move; 292 | if (chars & SectionHeader::CNT_INITIALIZED_DATA) this->opt->SizeOfInitializedData += move; 293 | if (chars & SectionHeader::CNT_UNINITIALIZED_DATA) this->opt->SizeOfUninitializedData += move; 294 | 295 | this->flush(); 296 | 297 | return sect; 298 | } 299 | dyn_ptr File::getExpandedSectionHdr(char *str, uint32_t room) { 300 | int i = 0; 301 | return this->getSectionHeader(str, &i) ? this->getExpandedSectionHdr(i, room) : dyn_ptr(); 302 | } 303 | dyn_ptr File::createSection(int i, const char *name, uint32_t room, SectionHeader::CharacteristicFlags chars) { 304 | // Check if section already exists. If it does, expand it and return it 305 | int j = 0; 306 | dyn_ptr sect = this->getSectionHeader(name, &j); 307 | if (sect) { return this->getExpandedSectionHdr(j, room); } 308 | 309 | // Get the file and section alignment values 310 | uint32_t salign = this->opt->SectionAlignment, falign = this->opt->FileAlignment; 311 | 312 | // Get general information about the header 313 | uint16_t nSects = this->header->NumberOfSections; 314 | dyn_ptr last_sect = this->sections + nSects - 1; 315 | uint32_t header_used_size = (uint32_t)((dyn_ptr)(last_sect+1) - this->data), header_raw_size = (uint32_t)roundUpTo(header_used_size, falign), header_space = header_raw_size - header_used_size; 316 | if (header_space < sizeof(SectionHeader)) { return nulldp; } // no room in header to store a new SectionHeader 317 | 318 | // Get information about where this new section will be placed 319 | bool at_end = i >= nSects, no_sects = nSects == 0; 320 | sect = at_end ? (no_sects ? dyn_ptr() : last_sect) : this->sections + i; 321 | if (at_end) i = nSects; 322 | uint32_t pos = at_end ? header_used_size : (uint32_t)((dyn_ptr)sect - this->data); 323 | 324 | // Get the size, position, and address of the new section 325 | uint32_t raw_size = (uint32_t)roundUpTo(room, falign), move_va = (uint32_t)roundUpTo(raw_size, salign); 326 | uint32_t va = (uint32_t)roundUpTo(at_end ? (no_sects ? header_used_size : sect->VirtualAddress + sect->VirtualSize) : sect->VirtualAddress, salign); 327 | uint32_t pntr = (uint32_t)roundUpTo(at_end ? (no_sects ? header_used_size : sect->PointerToRawData + sect->SizeOfRawData) : sect->PointerToRawData, falign); 328 | 329 | // Create the new section header 330 | SectionHeader s = { {0, 0, 0, 0, 0, 0, 0, 0}, 0, va, raw_size, pntr, 0, 0, 0, 0, chars }; 331 | size_t name_len = strlen(name); 332 | if (name_len >= ARRAYSIZE(s.Name)) 333 | memcpy(s.Name, name, ARRAYSIZE(s.Name)); 334 | else { 335 | memcpy(s.Name, name, name_len); 336 | memset(s.Name+name_len, 0, ARRAYSIZE(s.Name)-name_len); 337 | } 338 | 339 | // Increase file size (invalidates all local pointers to the file data) 340 | if (!this->setSize(this->data.size() + raw_size)) { return nulldp; } 341 | // cannot use sect or last_sect unless they are updated! 342 | 343 | // Shift data and fill space with zeros 344 | if (!this->shift(pntr, raw_size) || !this->zero(raw_size, pntr)) { return nulldp; } 345 | 346 | // Update the section headers 347 | if (!at_end && !this->move(pos, header_used_size-pos, sizeof(SectionHeader))) { return nulldp; } 348 | if (!this->set(&s, sizeof(SectionHeader), pos)) { return nulldp; } 349 | ++this->header->NumberOfSections; 350 | for (uint16_t si = (uint16_t)i + 1; si <= nSects; ++si) { // update the location and VA of all subsequent sections 351 | this->sections[si].VirtualAddress += move_va; 352 | this->sections[si].PointerToRawData += raw_size; 353 | } 354 | uint32_t ddCount = this->getDataDirectoryCount(); 355 | for (uint32_t d = 0; d < ddCount; ++d) { // update the VA of all subsequent data directories 356 | if (d == DataDirectory::SECURITY) { 357 | if (this->dataDir[d].VirtualAddress >= pntr) 358 | this->dataDir[d].VirtualAddress += raw_size; 359 | } else { 360 | if (this->dataDir[d].VirtualAddress >= va) 361 | this->dataDir[d].VirtualAddress += move_va; 362 | } 363 | } 364 | 365 | // Update NT header with new size information 366 | this->opt->SizeOfImage += raw_size; 367 | if (chars & SectionHeader::CNT_CODE) this->opt->SizeOfCode += raw_size; 368 | if (chars & SectionHeader::CNT_INITIALIZED_DATA) this->opt->SizeOfInitializedData += raw_size; 369 | if (chars & SectionHeader::CNT_UNINITIALIZED_DATA) this->opt->SizeOfUninitializedData += raw_size; 370 | 371 | this->flush(); 372 | 373 | return this->sections+i; 374 | } 375 | 376 | dyn_ptr File::createSection(const char *str, const char *name, uint32_t room, SectionHeader::CharacteristicFlags chars) { 377 | int i = this->header->NumberOfSections; 378 | return (str == NULL || this->getSectionHeader(str, &i)) ? this->createSection(i, name, room, chars) : dyn_ptr(); 379 | } 380 | dyn_ptr File::createSection(const char *name, uint32_t room, SectionHeader::CharacteristicFlags chars) { 381 | int i = this->header->NumberOfSections; 382 | this->getSectionHeader(".reloc", &i); // if it doesn't exist, i will remain unchanged 383 | return this->createSection(i, name, room, chars); 384 | } 385 | #pragma endregion 386 | 387 | #pragma region Size Functions 388 | /////////////////////////////////////////////////////////////////////////////// 389 | ///// Size Functions 390 | /////////////////////////////////////////////////////////////////////////////// 391 | size_t File::getSize() const { return this->data.size(); } 392 | bool File::setSize(size_t dwSize, bool grow_only) { 393 | if (this->data.isreadonly()) { return false; } 394 | if (dwSize == this->data.size() || (grow_only && dwSize < this->data.size())) { return true; } 395 | if (!this->data.resize(dwSize)) { this->unload(); return false; } 396 | return true; 397 | } 398 | #pragma endregion 399 | 400 | #pragma region Resource Shortcut Functions 401 | /////////////////////////////////////////////////////////////////////////////// 402 | ///// Resource Shortcut Functions 403 | /////////////////////////////////////////////////////////////////////////////// 404 | #ifdef EXPOSE_DIRECT_RESOURCES 405 | Rsrc *File::getResources() { return this->res; } 406 | const Rsrc *File::getResources() const { return this->res; } 407 | #endif 408 | bool File::resourceExists(const_resid type, const_resid name, uint16_t lang) const { return this->res->exists(type, name, lang); } 409 | bool File::resourceExists(const_resid type, const_resid name, uint16_t* lang) const { return this->res->exists(type, name, lang); } 410 | void* File::getResource(const_resid type, const_resid name, uint16_t lang, size_t* size) const { return this->res->get(type, name, lang, size); } 411 | void* File::getResource(const_resid type, const_resid name, uint16_t* lang, size_t* size) const { return this->res->get(type, name, lang, size); } 412 | bool File::removeResource(const_resid type, const_resid name, uint16_t lang) { return !this->data.isreadonly() && this->res->remove(type, name, lang); } 413 | bool File::addResource(const_resid type, const_resid name, uint16_t lang, const void* dat, size_t size, Overwrite overwrite) { return !this->data.isreadonly() && this->res->add(type, name, lang, dat, size, overwrite); } 414 | #pragma endregion 415 | 416 | #pragma region Direct Data Functions 417 | /////////////////////////////////////////////////////////////////////////////// 418 | ///// Direct Data Functions 419 | /////////////////////////////////////////////////////////////////////////////// 420 | dyn_ptr File::get(uint32_t dwOffset, uint32_t *dwSize) { if (dwSize) *dwSize = (uint32_t)this->data.size() - dwOffset; return this->data + dwOffset; } 421 | const dyn_ptr File::get(uint32_t dwOffset, uint32_t *dwSize) const { if (dwSize) *dwSize = (uint32_t)this->data.size() - dwOffset; return this->data + dwOffset; } 422 | bool File::set(const void* lpBuffer, uint32_t dwSize, uint32_t dwOffset) { return !this->data.isreadonly() && (dwOffset + dwSize <= this->data.size()) && memcpy(this->data + dwOffset, lpBuffer, dwSize); } 423 | bool File::zero(uint32_t dwSize, uint32_t dwOffset) { return !this->data.isreadonly() && (dwOffset + dwSize <= this->data.size()) && memset(this->data + dwOffset, 0, dwSize); } 424 | bool File::move(uint32_t dwOffset, uint32_t dwSize, int32_t dwDistanceToMove) { return !this->data.isreadonly() && (dwOffset + dwSize + dwDistanceToMove <= this->data.size()) && memmove(this->data+dwOffset+dwDistanceToMove, this->data+dwOffset, dwSize); } 425 | bool File::shift(uint32_t dwOffset, int32_t dwDistanceToMove) { return move(dwOffset, (uint32_t)this->data.size() - dwOffset - dwDistanceToMove, dwDistanceToMove); } 426 | bool File::flush() { return this->data.flush(); } 427 | #pragma endregion 428 | 429 | #pragma region General Query and Settings Functions 430 | /////////////////////////////////////////////////////////////////////////////// 431 | ///// General Query and Settings Functions 432 | /////////////////////////////////////////////////////////////////////////////// 433 | #define CHK_SUM_FOLD(c) (((c)&0xffff) + ((c)>>16)) 434 | #define CHK_SUM_OFFSET (peOffset+sizeof(uint32_t)+sizeof(FileHeader)+offsetof(OptionalHeader, CheckSum)) 435 | bool File::UpdatePEChkSum(bytes data, size_t dwSize, size_t peOffset, uint32_t dwOldCheck) { 436 | uint16_t *ptr = (uint16_t*)data; 437 | size_t len = dwSize/sizeof(uint16_t); 438 | uint32_t c = 0; 439 | while (len) { 440 | size_t l = (len < 0x4000) ? len : 0x4000; 441 | len -= l; 442 | for (size_t j=0; jdata.isreadonly() && UpdatePEChkSum(this->data+0, this->data.size(), this->peOffset, this->is64bit() ? this->nth64->OptionalHeader.CheckSum : this->nth32->OptionalHeader.CheckSum) && this->flush(); } 458 | //------------------------------------------------------------------------------ 459 | static const byte TinyDosStub[] = {0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x57, 0x69, 0x6E, 0x20, 0x4F, 0x6E, 0x6C, 0x79, 0x0D, 0x0A, 0x24, 0x00, 0x00, 0x00}; 460 | bool File::hasExtraData() const { return this->dosh->e_crlc == 0x0000 && this->dosh->e_cparhdr == 0x0002 && this->dosh->e_lfarlc == 0x0020; } 461 | dyn_ptr File::getExtraData(uint32_t *size) { 462 | uint32_t sz = this->peOffset - sizeof(DOSHeader); 463 | if (!this->hasExtraData()) { 464 | if (this->data.isreadonly()) { return nulldp; } 465 | // Create the new header and fill the old stub in with 0s 466 | this->dosh->e_crlc = 0x0000; // number of relocations 467 | this->dosh->e_cparhdr = 0x0002; // size of header in 16 byte paragraphs 468 | this->dosh->e_lfarlc = 0x0020; // location of relocation table (end of tiny header) 469 | memcpy((dyn_ptr)this->dosh+0x20, TinyDosStub, sizeof(TinyDosStub)); 470 | memset(this->data + sizeof(DOSHeader), 0, sz); 471 | this->flush(); 472 | } 473 | *size = sz; 474 | return this->data + sizeof(DOSHeader); 475 | } 476 | //------------------------------------------------------------------------------ 477 | bool File::clearCertificateTable() { 478 | if (this->data.isreadonly()) { return false; } 479 | DataDirectory d = this->dataDir[DataDirectory::SECURITY]; 480 | if (d.VirtualAddress && d.Size) { 481 | // Zero out the certificate 482 | memset(this->data + d.VirtualAddress, 0, d.Size); 483 | 484 | // Find out if the certificate was at the end 485 | uint32_t i; 486 | for (i = d.VirtualAddress + d.Size; i < this->data.size() && !this->data[i-1]; ++i); 487 | if (i >= this->data.size() && !this->setSize(d.VirtualAddress, false)) 488 | return false; 489 | 490 | // Update the header 491 | this->dataDir[DataDirectory::SECURITY].VirtualAddress = 0; 492 | this->dataDir[DataDirectory::SECURITY].Size = 0; 493 | 494 | // Flush the changes 495 | this->flush(); 496 | } 497 | return true; 498 | } 499 | //------------------------------------------------------------------------------ 500 | PE::Version::Version File::getFileVersion() const { return this->version; } 501 | //------------------------------------------------------------------------------ 502 | bool File::isAlreadyModified() const { return this->modified; } 503 | bool File::setModifiedFlag() { 504 | if (!this->data.isreadonly() && !this->modified) { 505 | const_resid name = NULL; 506 | uint16_t lang = 0; 507 | size_t size = 0; 508 | void* ver = GetResourceDirectInRsrc(this->data+0, this->getSectionHeader(".rsrc"), ResType::VERSION, FIRST_ENTRY, &name, &lang, &size); 509 | FileVersionBasicInfo *v = FileVersionBasicInfo::Get(ver); 510 | if (v) { 511 | v->FileFlags = (FileVersionBasicInfo::Flags)(v->FileFlags | (v->FileFlagsMask & (FileVersionBasicInfo::PATCHED | FileVersionBasicInfo::SPECIALBUILD))); 512 | this->modified = this->res->add(ResType::VERSION, name, lang, ver, size, ONLY); 513 | this->flush(); 514 | } 515 | } 516 | return this->modified; 517 | } 518 | //----------------------------------------------------------------------------- 519 | typedef union _Reloc { 520 | uint16_t Reloc; 521 | struct { 522 | uint16_t Offset : 12; 523 | uint16_t Type : 4; 524 | }; 525 | } Reloc; 526 | #define RELOCS(e) (dyn_ptr)((dyn_ptr)e+sizeof(BaseRelocation)) 527 | #define NEXT_RELOCS(e) (dyn_ptr)((dyn_ptr)e+e->SizeOfBlock) 528 | #define COUNT_RELOCS(e) (e->SizeOfBlock - sizeof(BaseRelocation)) / sizeof(uint16_t) 529 | bool File::removeRelocs(uint32_t start, uint32_t end, bool reverse) { 530 | if (end < start) { return false; } 531 | 532 | // Read the relocs 533 | //DataDirectory dir = f->getDataDirectory(DataDirectory::BASERELOC); 534 | //uint32 size = dir.Size, pntr = dir.VirtualAddress; 535 | dyn_ptr sect = this->getSectionHeader(".reloc"); 536 | if (!sect) { return true; } // no relocations exist, so nothing to remove! 537 | 538 | uint32_t size = sect->SizeOfRawData, pntr = sect->PointerToRawData; 539 | dyn_ptr dat = this->get(pntr); 540 | 541 | //ABSOLUTE = IMAGE_REL_I386_ABSOLUTE or IMAGE_REL_AMD64_ABSOLUTE 542 | //HIGHLOW => ??? or IMAGE_REL_AMD64_ADDR32NB (32-bit address w/o image base (RVA)) 543 | //DIR64 => IMAGE_REL_AMD64_SSPAN32 (32 bit signed span-dependent value applied at link time) 544 | uint16_t new_type = reverse ? (this->is64bit() ? BaseRelocation::DIR64 : BaseRelocation::HIGHLOW) : BaseRelocation::ABSOLUTE; 545 | 546 | // Remove everything that is between start and end 547 | // We do a thorough search for possible relocations and do not assume that they are in order 548 | dyn_ptr entry_end = dat + size; 549 | for (dyn_ptr entry = (dyn_ptr)dat; entry < entry_end && entry->SizeOfBlock > 0; entry = NEXT_RELOCS(entry)) { 550 | 551 | // Check that the ranges overlap 552 | if (entry->VirtualAddress+0xFFF < start || entry->VirtualAddress > end) continue; 553 | 554 | // Go through each reloc in this entry 555 | uint32_t count = COUNT_RELOCS(entry); 556 | dyn_ptr relocs = RELOCS(entry); 557 | for (uint32_t i = 0; i < count; ++i) { 558 | // Already 'removed' 559 | if ((!reverse && relocs[i].Type == BaseRelocation::ABSOLUTE) || 560 | (reverse && (relocs[i].Type != BaseRelocation::ABSOLUTE || relocs[i].Offset == 0))) continue; 561 | 562 | // Check the virtual address and possibly clear it 563 | uint32_t va = entry->VirtualAddress + relocs[i].Offset; 564 | if (va >= start && va <= end) { 565 | //relocs[i].Reloc = 0; 566 | relocs[i].Type = new_type; 567 | } 568 | } 569 | } 570 | return true; 571 | } 572 | #pragma endregion 573 | 574 | #pragma region Saving Functions 575 | /////////////////////////////////////////////////////////////////////////////// 576 | ///// Saving Functions 577 | /////////////////////////////////////////////////////////////////////////////// 578 | size_t File::getSizeOf(uint32_t cnt, int rsrcIndx, size_t rsrcRawSize) const { 579 | size_t size = 0; 580 | for (uint16_t i = 0; i < this->header->NumberOfSections; i++) 581 | if (this->sections[i].Characteristics & cnt) 582 | size += (i == (uint16_t)rsrcIndx) ? rsrcRawSize : this->sections[i].SizeOfRawData; 583 | return size; 584 | } 585 | inline static void adjustAddr(uint32_t &addr, size_t rAddr, size_t rNewSize, size_t rOldSize) { 586 | if (addr != 0 && addr >= rAddr + rOldSize) 587 | addr = (uint32_t)((addr + rNewSize) - rOldSize); // subtraction needs to be last b/c these are unsigned 588 | } 589 | bool File::save() { 590 | if (this->data.isreadonly()) { return false; } 591 | 592 | // Compile the .rsrc, get its size, and get all the information about it 593 | bool is64bit = this->is64bit(); 594 | uint32_t fAlign = is64bit ? this->getNtHeaders64()->OptionalHeader.FileAlignment : this->getNtHeaders32()->OptionalHeader.FileAlignment; 595 | uint32_t sAlign = is64bit ? this->getNtHeaders64()->OptionalHeader.SectionAlignment : this->getNtHeaders32()->OptionalHeader.SectionAlignment; 596 | int rIndx = 0; 597 | dyn_ptr rSect = this->getSectionHeader(".rsrc", &rIndx); 598 | if (!rSect) { 599 | this->createSection(".rsrc", 0, INIT_DATA_SECTION_R); 600 | rSect = this->getSectionHeader(".rsrc", &rIndx); 601 | } 602 | size_t rSize = 0; 603 | void* rsrc = this->res->compile(&rSize, rSect->VirtualAddress); 604 | size_t rRawSize = roundUpTo(rSize, fAlign); 605 | size_t rVirSize = roundUpTo(rSize, sAlign); 606 | //size_t rSizeOld = rSect->Misc.VirtualSize; 607 | size_t rRawSizeOld = rSect->SizeOfRawData; 608 | size_t rVirSizeOld = roundUpTo(rSect->VirtualSize, sAlign); 609 | uint32_t pntr = rSect->PointerToRawData; 610 | uint32_t imageSize = 0; //, imageSizeOld = 0; 611 | uint32_t fileSize = 0, fileSizeOld = (uint32_t)this->data.size(); 612 | 613 | // Update PointerToSymbolTable 614 | adjustAddr(this->header->PointerToSymbolTable, rSect->VirtualAddress, rVirSize, rVirSizeOld); 615 | 616 | // Update Optional Header 617 | this->opt->SizeOfInitializedData = (uint32_t)this->getSizeOf(SectionHeader::CNT_INITIALIZED_DATA, rIndx, rRawSize); 618 | adjustAddr(this->opt->AddressOfEntryPoint, rSect->VirtualAddress, rVirSize, rVirSizeOld); 619 | adjustAddr(this->opt->BaseOfCode, rSect->VirtualAddress, rVirSize, rVirSizeOld); 620 | //imageSizeOld = this->opt->SizeOfImage; 621 | 622 | // Update the Data Directories 623 | this->dataDir[DataDirectory::RESOURCE].Size = (uint32_t)rSize; 624 | uint32_t ddCount = this->getDataDirectoryCount(); 625 | for (uint32_t i = 0; i < ddCount; i++) { 626 | if (i == DataDirectory::SECURITY) { // the virtual address of DataDirectory::SECURITY is actually a file address, not a virtual address 627 | adjustAddr(this->dataDir[i].VirtualAddress, rSect->PointerToRawData, rRawSize, rRawSizeOld); 628 | if (this->dataDir[i].VirtualAddress + this->dataDir[i].Size > fileSize) 629 | fileSize = this->dataDir[i].VirtualAddress + this->dataDir[i].Size; 630 | } else { 631 | adjustAddr(this->dataDir[i].VirtualAddress, rSect->VirtualAddress, rVirSize, rVirSizeOld); 632 | if (this->dataDir[i].VirtualAddress + this->dataDir[i].Size > imageSize) 633 | imageSize = this->dataDir[i].VirtualAddress + this->dataDir[i].Size; 634 | } 635 | } 636 | 637 | // Update all section headers 638 | for (uint16_t i = (uint16_t)rIndx; i < this->header->NumberOfSections; i++) { 639 | if (strncmp((const char*)this->sections[i].Name, ".rsrc", ARRAYSIZE(this->sections[i].Name)) == 0) { 640 | this->sections[i].VirtualSize = (uint32_t)rSize; 641 | this->sections[i].SizeOfRawData = (uint32_t)rRawSize; 642 | } else { 643 | adjustAddr(this->sections[i].VirtualAddress, rSect->VirtualAddress, rVirSize, rVirSizeOld); 644 | adjustAddr(this->sections[i].PointerToRawData, rSect->PointerToRawData, rRawSize, rRawSizeOld); 645 | } 646 | adjustAddr(this->sections[i].PointerToLinenumbers, rSect->PointerToRawData, rRawSize, rRawSizeOld); 647 | adjustAddr(this->sections[i].PointerToRelocations, rSect->PointerToRawData, rRawSize, rRawSizeOld); 648 | if (this->sections[i].VirtualAddress + this->sections[i].VirtualSize > imageSize) 649 | imageSize = this->sections[i].VirtualAddress + this->sections[i].VirtualSize; 650 | if (this->sections[i].PointerToRawData + this->sections[i].SizeOfRawData > fileSize) 651 | fileSize = this->sections[i].PointerToRawData + this->sections[i].SizeOfRawData; 652 | } 653 | 654 | // Update the ImageSize 655 | this->opt->SizeOfImage = (uint32_t)roundUpTo(imageSize, sAlign); 656 | 657 | // Increase file size (invalidates all local pointers to the file data) 658 | if (fileSize > fileSizeOld && !this->setSize(fileSize)) { free(rsrc); return false; } 659 | 660 | // Move all sections after resources and save resources 661 | dyn_ptr dp = this->data+pntr; 662 | if (rRawSize != rRawSizeOld && fileSize-rRawSize-pntr > 0) 663 | memmove(dp+rRawSize, dp+rRawSizeOld, fileSize-rRawSize-pntr); 664 | if (rRawSize > rSize) 665 | memset(dp+rSize, 0, rRawSize-rSize); 666 | memcpy(dp, rsrc, rSize); 667 | free(rsrc); 668 | 669 | // Decrease file size (invalidates all local pointers to the file data) 670 | if (fileSize < fileSizeOld && !this->setSize(fileSize, false)) { return false; } 671 | 672 | // Finish Up 673 | return updatePEChkSum(); 674 | } 675 | #pragma endregion 676 | -------------------------------------------------------------------------------- /PEFile.h: -------------------------------------------------------------------------------- 1 | // pe-file: library for reading and manipulating pe-files 2 | // Copyright (C) 2012 Jeffrey Bush jeff@coderforlife.com 3 | // 4 | // This library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | // Implements the classes and functions for dealing with general features of PE files 18 | 19 | #ifndef PE_FILE_H 20 | #define PE_FILE_H 21 | 22 | #define EXPOSE_DIRECT_RESOURCES 23 | 24 | #include "PEDataTypes.h" 25 | #include "PEFileResources.h" 26 | #include "PEDataSource.h" 27 | #include "PEVersion.h" 28 | 29 | namespace PE { 30 | 31 | class File { 32 | protected: 33 | PE::DataSource data; 34 | 35 | dyn_ptr dosh; 36 | long peOffset; 37 | dyn_ptr nth32; 38 | dyn_ptr nth64; 39 | dyn_ptr header; // part of nth32/nth64 header 40 | dyn_ptr opt; // part of nth32/nth64 header 41 | dyn_ptr dataDir; // part of nth32/nth64 header 42 | dyn_ptr sections; 43 | Rsrc *res; 44 | 45 | PE::Version::Version version; 46 | bool modified; 47 | 48 | size_t getSizeOf(uint32_t cnt, int rsrcIndx, size_t rsrcRawSize) const; 49 | 50 | bool load(); 51 | void unload(); 52 | public: 53 | File(void* data, size_t size, bool readonly = false); // data is freed when the PEFile is deleted 54 | File(const_str filename, bool readonly = false); 55 | File(DataSource data); 56 | ~File(); 57 | bool isLoaded() const; 58 | bool isReadOnly() const; 59 | 60 | bool save(); // flushes 61 | 62 | bool is32bit() const; 63 | bool is64bit() const; 64 | uint64_t getImageBase() const; 65 | 66 | dyn_ptr getFileHeader(); // pointer can modify the file 67 | dyn_ptr getNtHeaders32(); // pointer can modify the file 68 | dyn_ptr getNtHeaders64(); // pointer can modify the file 69 | const dyn_ptr getFileHeader() const; 70 | const dyn_ptr getNtHeaders32() const; 71 | const dyn_ptr getNtHeaders64() const; 72 | 73 | uint32_t getDataDirectoryCount() const; 74 | dyn_ptr getDataDirectory(int i); // pointer can modify the file 75 | const dyn_ptr getDataDirectory(int i) const; 76 | 77 | dyn_ptr getSectionHeader(int i); // pointer can modify the file 78 | dyn_ptr getSectionHeader(const char *str, int *i = NULL); // pointer can modify the file 79 | dyn_ptr getSectionHeaderByRVA(uint32_t rva, int *i); // pointer can modify the file 80 | dyn_ptr getSectionHeaderByVA(uint64_t va, int *i); // pointer can modify the file 81 | const dyn_ptr getSectionHeader(int i) const; 82 | const dyn_ptr getSectionHeader(const char *str, int *i = NULL) const; 83 | const dyn_ptr getSectionHeaderByRVA(uint32_t rva, int *i) const; 84 | const dyn_ptr getSectionHeaderByVA(uint64_t va, int *i) const; 85 | int getSectionHeaderCount() const; 86 | 87 | dyn_ptr getExpandedSectionHdr(int i, uint32_t room); // pointer can modify the file, invalidates all pointers returned by functions, flushes 88 | dyn_ptr getExpandedSectionHdr(char *str, uint32_t room); // as above 89 | 90 | static const Image::SectionHeader::CharacteristicFlags CHARS_CODE_SECTION = (Image::SectionHeader::CharacteristicFlags)(Image::SectionHeader::CNT_CODE | Image::SectionHeader::MEM_EXECUTE | Image::SectionHeader::MEM_READ); 91 | static const Image::SectionHeader::CharacteristicFlags INIT_DATA_SECTION_R = (Image::SectionHeader::CharacteristicFlags)(Image::SectionHeader::CNT_INITIALIZED_DATA | Image::SectionHeader::MEM_READ); 92 | static const Image::SectionHeader::CharacteristicFlags INIT_DATA_SECTION_RW = (Image::SectionHeader::CharacteristicFlags)(Image::SectionHeader::CNT_INITIALIZED_DATA | Image::SectionHeader::MEM_READ | Image::SectionHeader::MEM_WRITE); 93 | 94 | dyn_ptr createSection(int i, const char *name, uint32_t room, Image::SectionHeader::CharacteristicFlags chars); // pointer can modify the file, invalidates all pointers returned by functions, flushes 95 | dyn_ptr createSection(const char *str, const char *name, uint32_t room, Image::SectionHeader::CharacteristicFlags chars); // as above, adds before the section named str 96 | dyn_ptr createSection(const char *name, uint32_t room, Image::SectionHeader::CharacteristicFlags chars); // as above, adds before ".reloc" if exists or at the very end 97 | 98 | size_t getSize() const; 99 | bool setSize(size_t dwSize, bool grow_only = true); // invalidates all pointers returned by functions, flushes 100 | 101 | dyn_ptr get(uint32_t dwOffset = 0, uint32_t *dwSize = NULL); // pointer can modify the file 102 | const dyn_ptr get(uint32_t dwOffset = 0, uint32_t *dwSize = NULL) const; 103 | bool set(const void* lpBuffer, uint32_t dwSize, uint32_t dwOffset); // shorthand for memcpy(f->get(dwOffset), lpBuffer, dwSize) with bounds checking 104 | bool zero(uint32_t dwSize, uint32_t dwOffset); // shorthand for memset(f->get(dwOffset), 0, dwSize) with bounds checking 105 | bool move(uint32_t dwOffset, uint32_t dwSize, int32_t dwDistanceToMove);// shorthand for x = f->get(dwOffset); memmove(x+dwDistanceToMove, x, dwSize) with bounds checking 106 | bool shift(uint32_t dwOffset, int32_t dwDistanceToMove); // shorthand for f->move(dwOffset, f->getSize() - dwOffset - dwDistanceToMove, dwDistanceToMove) 107 | bool flush(); 108 | 109 | bool updatePEChkSum(); // flushes 110 | bool hasExtraData() const; 111 | dyn_ptr getExtraData(uint32_t *size); // pointer can modify the file, when first enabling it will flush 112 | bool clearCertificateTable(); // may invalidate all pointers returned by functions, flushes 113 | PE::Version::Version getFileVersion() const; 114 | bool isAlreadyModified() const; 115 | bool setModifiedFlag(); // flushes 116 | bool removeRelocs(uint32_t start, uint32_t end, bool reverse = false); 117 | 118 | #ifdef EXPOSE_DIRECT_RESOURCES 119 | Rsrc *getResources(); 120 | const Rsrc *getResources() const; 121 | #endif 122 | bool resourceExists(const_resid type, const_resid name, uint16_t lang) const; 123 | bool resourceExists(const_resid type, const_resid name, uint16_t* lang = NULL) const; 124 | void* getResource (const_resid type, const_resid name, uint16_t lang, size_t* size) const; // must be freed 125 | void* getResource (const_resid type, const_resid name, uint16_t* lang, size_t* size) const; // must be freed 126 | bool removeResource(const_resid type, const_resid name, uint16_t lang); 127 | bool addResource (const_resid type, const_resid name, uint16_t lang, const void* data, size_t size, Overwrite overwrite = ALWAYS); 128 | 129 | static void* GetResourceDirect(void* data, const_resid type, const_resid name); // must be freed, massively performance enhanced for a single retrieval, no editing, and no buffer checks // lang? size? 130 | static bool UpdatePEChkSum(bytes data, size_t dwSize, size_t peOffset, uint32_t dwOldCheck); 131 | }; 132 | 133 | } 134 | 135 | #endif -------------------------------------------------------------------------------- /PEFileResources.cpp: -------------------------------------------------------------------------------- 1 | // pe-file: library for reading and manipulating pe-files 2 | // Copyright (C) 2012 Jeffrey Bush jeff@coderforlife.com 3 | // 4 | // This library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #ifdef __cplusplus_cli 18 | #pragma unmanaged 19 | #endif 20 | 21 | #define _DECLARE_ALL_PE_FILE_RESOURCES_ 22 | #include "PEFileResources.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | using namespace PE; 30 | using namespace PE::Image; 31 | using namespace PE::Internal; 32 | 33 | // Raised when resources have a problem loading 34 | class ResLoadFailure {}; 35 | static ResLoadFailure resLoadFailure; 36 | 37 | bool ResCmp::operator()(const_resid a, const_resid b) const { return IsIntResID(a) ? (IsIntResID(b) ? (ResID2Int(a) < ResID2Int(b)) : false) : (IsIntResID(b) ? true : wcscmp(a, b) < 0); } 38 | //int ResCmp::operator()(const_resid a, const_resid b) const { return (IsIntResID(a) ? (IsIntResID(b) ? ((uint16)a - (uint16)b) : 1) : (IsIntResID(b) ? -1 : wcscmp(a, b)); } 39 | 40 | inline static resid dup(const_resid id) { return IsIntResID(id) ? MakeResID(ResID2Int(id)) : _wcsdup(id); } 41 | inline static void free_id(resid id) { if (!IsIntResID(id)) free(id); } 42 | 43 | #pragma region RSRC Utility Functions 44 | 45 | static ResourceDirectoryEntry *GetEntries(const_bytes data, size_t size, size_t offset, uint32_t *nEntries) { 46 | if (offset + sizeof(ResourceDirectory) >= size) { throw resLoadFailure; } 47 | ResourceDirectory dir = *(ResourceDirectory*)(data+offset); 48 | *nEntries = dir.NumberOfIdEntries+dir.NumberOfNamedEntries; 49 | offset += sizeof(ResourceDirectory); 50 | if (offset + (*nEntries)*sizeof(ResourceDirectoryEntry) >= size) { throw resLoadFailure; } 51 | return (ResourceDirectoryEntry*)(data+offset); 52 | } 53 | static resid GetResourceName(const_bytes data, size_t size, size_t offset, const ResourceDirectoryEntry & entry) { 54 | if (entry.NameIsString) { 55 | offset += entry.NameOffset; 56 | 57 | if (offset + sizeof(uint16_t) > size) { return NULL; } 58 | uint16_t len = *(uint16_t*)(data+offset); 59 | offset += sizeof(uint16_t); 60 | 61 | if (offset + sizeof(wchar_t)*len > size) { return NULL; } 62 | resid str = wcsncpy((wchar_t*)malloc((len+1)*sizeof(wchar_t)), (wchar_t*)(data+offset), len); 63 | str[len] = 0; 64 | 65 | return str; 66 | } else { 67 | return MakeResID(entry.Id); 68 | } 69 | } 70 | static void WriteResDir(bytes data, size_t& pos, uint16_t nNamed, uint16_t nId) { 71 | ResourceDirectory dir; 72 | dir.Characteristics = 0; 73 | dir.TimeDateStamp = 0; 74 | dir.MajorVersion = 4; 75 | dir.MinorVersion = 0; 76 | dir.NumberOfNamedEntries = nNamed; 77 | dir.NumberOfIdEntries = nId; 78 | memcpy(data+pos, &dir, sizeof(ResourceDirectory)); 79 | pos += sizeof(ResourceDirectory); 80 | } 81 | template 82 | static void WriteResDir(bytes data, size_t &pos, Iterator start, Iterator end) { 83 | uint16_t nNamed = 0, nId = 0; 84 | for (Iterator i = start; i != end; ++i) { 85 | if (IsIntResID(i->first)) 86 | nId += 1; 87 | else 88 | nNamed += 1; 89 | } 90 | WriteResDir(data, pos, nNamed, nId); 91 | } 92 | static void WriteResDirEntry(bytes data, const_resid name, size_t headerSize, size_t &pos, size_t &posDir, size_t &posData) { 93 | ResourceDirectoryEntry entry; 94 | entry.DataIsDirectory = 1; 95 | entry.OffsetToDirectory = posDir; 96 | posDir += headerSize; 97 | 98 | entry.NameIsString = !IsIntResID(name); 99 | if (entry.NameIsString) { 100 | entry.NameOffset = posData; 101 | uint16_t len = (uint16_t)wcslen(name); 102 | memcpy(data+posData, &len, sizeof(uint16_t)); 103 | memcpy(data+posData+sizeof(uint16_t), name, len*sizeof(wchar_t)); 104 | posData += roundUpTo<4>(len*sizeof(wchar_t)+sizeof(uint16_t)); 105 | } else { 106 | entry.Name = (uint32_t)ResID2Int(name); 107 | } 108 | 109 | memcpy(data+pos, &entry, sizeof(ResourceDirectoryEntry)); 110 | pos += sizeof(ResourceDirectoryEntry); 111 | } 112 | #pragma endregion 113 | 114 | #pragma region RES Utility Functions 115 | 116 | typedef struct _RESHEADER { 117 | uint32_t DataSize; 118 | uint32_t HeaderSize; 119 | resid Type; 120 | resid Name; 121 | uint32_t DataVersion; 122 | uint16_t MemoryFlags; 123 | uint16_t LanguageId; 124 | uint32_t Version; 125 | uint32_t Characteristics; 126 | } RESHeader; 127 | 128 | static const size_t RESHeaderSize = sizeof(uint32_t)*7 + sizeof(uint16_t)*2; 129 | 130 | static size_t ReadRESHeaderID(const_bytes data, resid *id) { 131 | wchar_t* chars = (wchar_t*)data; 132 | size_t len; 133 | if (chars[0] == 0xFFFF) { 134 | len = 2 * sizeof(wchar_t); 135 | *id = MakeResID(chars[1]); 136 | } else { 137 | len = (wcslen((resid)chars) + 1) * sizeof(wchar_t); 138 | *id = (resid)chars; 139 | } 140 | return len; 141 | } 142 | static RESHeader* ReadRESHeader(const_bytes data, size_t size, size_t& pos) { 143 | if (pos + sizeof(uint32_t)*2 >= size) { return NULL; } 144 | 145 | RESHeader *h = (RESHeader*)malloc(sizeof(RESHeader)); 146 | memcpy(h, data+pos, sizeof(uint32_t)*2); 147 | 148 | if (pos + h->HeaderSize >= size) { free(h); return NULL; } 149 | 150 | pos += sizeof(uint32_t)*2; 151 | pos += ReadRESHeaderID(data + pos, &h->Type); 152 | pos += ReadRESHeaderID(data + pos, &h->Name); 153 | 154 | memcpy(&h->DataVersion, data + pos, sizeof(RESHeader) - offsetof(RESHeader, DataVersion)); 155 | pos += sizeof(RESHeader) - offsetof(RESHeader, DataVersion); 156 | 157 | return h; 158 | } 159 | 160 | static const uint16_t DefResMemoryFlags = 0x0030; // or possibly 0x0000 ? 161 | static const uint16_t ResMemoryFlags[] = { 162 | 0x0000, // Nothing 163 | 0x1010, // Cursor 164 | 0x0030, // Bitmap 165 | 0x1010, // Icon 166 | 0x1030, // Menu 167 | 0x1030, // Dialog 168 | 0x1030, // String 169 | 0x1030, // Fontdir 170 | 0x1030, // Font 171 | 0x0030, // Accelerator 172 | 0x0030, // RCData 173 | 0x0030, // Message Table 174 | 0x1030, // Group Cursor 175 | DefResMemoryFlags, 176 | 0x1030, // Group Icon 177 | DefResMemoryFlags, 178 | 0x0030, // Version 179 | 0x1030, // Dlg Include (no examples) 180 | DefResMemoryFlags, 181 | 0x0030, // Plug Play (no examples) (obsolete) 182 | 0x0030, // VXD (obsolete) 183 | 0x0030, // Ani-Cursor 184 | 0x0030, // Ani-Icon (no examples) 185 | 0x0030, // HTML 186 | 0x0030, // Manifest 187 | }; 188 | 189 | static size_t GetRESHeaderIDExtraLen(const_resid id) { return IsIntResID(id) ? 0 : (wcslen(id) - 1) * sizeof(wchar_t); } 190 | static size_t WriteRESHeaderID(bytes data, const_resid id, uint32_t *hdrSize) { 191 | if (IsIntResID(id)) { 192 | *((uint32_t*)data) = (((uint32_t)ResID2Int(id)) << 16) | 0xFFFF; 193 | return 0; 194 | } else { 195 | size_t len = (wcslen(id) + 1) * sizeof(wchar_t); 196 | memcpy(data, id, len); 197 | len -= sizeof(uint32_t); 198 | *hdrSize += (uint32_t)len; 199 | return len; 200 | } 201 | } 202 | static size_t WriteRESHeader(bytes data, const_resid type, const_resid name, uint16_t lang, size_t dataSize) { 203 | *(uint32_t*)data = (uint32_t)dataSize; 204 | uint32_t *hdrSize = ((uint32_t*)data)+1; 205 | *hdrSize = (uint32_t)RESHeaderSize; 206 | data += sizeof(uint32_t)*2; 207 | data += WriteRESHeaderID(data, type, hdrSize) + sizeof(uint32_t); 208 | data += WriteRESHeaderID(data, name, hdrSize) + sizeof(uint32_t); 209 | memset(data, 0, sizeof(uint32_t)*3 + sizeof(uint16_t)*2); // DataVersion, *, Version, Characteristics 210 | ((uint16_t*)data)[2] = (IsIntResID(type) && (ResID2Int(type) < ARRAYSIZE(ResMemoryFlags))) ? ResMemoryFlags[ResID2Int(type)] : DefResMemoryFlags; // MemoryFlags 211 | ((uint16_t*)data)[3] = lang; 212 | return *hdrSize; 213 | } 214 | #pragma endregion 215 | 216 | #pragma region Rsrc 217 | /////////////////////////////////////////////////////////////////////////////// 218 | ///// Rsrc 219 | /////////////////////////////////////////////////////////////////////////////// 220 | Rsrc* Rsrc::createFromRSRCSection(const_bytes data, size_t size, SectionHeader *section) { try { return (!data || !size || !section) ? NULL : new Rsrc(data, size, section); } catch (ResLoadFailure&) { return NULL; } } 221 | Rsrc::Rsrc(const_bytes data, size_t size, SectionHeader *section) { 222 | uint32_t nEntries; 223 | ResourceDirectoryEntry *entries = GetEntries(data, size, section->PointerToRawData, &nEntries); 224 | for (uint16_t i = 0; i < nEntries; i++) { 225 | resid type = GetResourceName(data, size, section->PointerToRawData, entries[i]); 226 | //this->types.set(type), new ResourceType(type, data, size, section->PointerToRawData, section->VirtualAddress, entries[i])); 227 | this->types[type] = new ResourceType(type, data, size, section->PointerToRawData, section->VirtualAddress, entries[i]); 228 | } 229 | this->cleanup(); 230 | } 231 | Rsrc* Rsrc::createFromRESFile(const_bytes data, size_t size) { try { return (!data || !size) ? NULL : new Rsrc(data, size); } catch (ResLoadFailure&) { return NULL; } } 232 | Rsrc::Rsrc(const_bytes data, size_t size) { 233 | RESHeader *h; 234 | size_t pos = 0; 235 | while ((h = ReadRESHeader(data, size, pos)) != NULL) { 236 | if (h->Type || h->Name || h->LanguageId || h->DataSize) { 237 | this->add(h->Type, h->Name, h->LanguageId, data + pos, h->DataSize); 238 | pos += roundUpTo<4>(h->HeaderSize + h->DataSize) - h->HeaderSize; 239 | } 240 | } 241 | this->cleanup(); 242 | } 243 | Rsrc* Rsrc::createEmpty() { return new Rsrc(); } 244 | Rsrc::Rsrc() { } 245 | Rsrc::~Rsrc() { 246 | for (TypeMap::const_iterator i = this->types.begin(); i != this->types.end(); ++i) { 247 | free_id(i->first); 248 | delete i->second; 249 | } 250 | this->types.clear(); 251 | } 252 | const_resid Rsrc::getId() const { return NULL; } 253 | bool Rsrc::cleanup() { 254 | for (TypeMap::iterator i = this->types.begin(); i != this->types.end(); ) { 255 | if (i->second->cleanup()) { 256 | free_id(i->first); 257 | delete i->second; 258 | this->types.erase(i++); 259 | } else { ++i; } 260 | } 261 | return this->isEmpty(); 262 | } 263 | bool Rsrc::exists(const_resid type, const_resid name, uint16_t lang) const { 264 | TypeMap::const_iterator iter = this->types.find((resid)type); 265 | return iter == this->types.end() ? false : iter->second->exists(name, lang); 266 | } 267 | bool Rsrc::exists(const_resid type, const_resid name, uint16_t *lang) const { 268 | TypeMap::const_iterator iter = this->types.find((resid)type); 269 | return iter == this->types.end() ? false : iter->second->exists(name, lang); 270 | } 271 | void* Rsrc::get(const_resid type, const_resid name, uint16_t lang, size_t *size) const { 272 | TypeMap::const_iterator iter = this->types.find((resid)type); 273 | return iter == this->types.end() ? NULL : iter->second->get(name, lang, size); 274 | } 275 | void* Rsrc::get(const_resid type, const_resid name, uint16_t *lang, size_t *size) const { 276 | TypeMap::const_iterator iter = this->types.find((resid)type); 277 | return iter == this->types.end() ? NULL : iter->second->get(name, lang, size); 278 | } 279 | bool Rsrc::remove(const_resid type, const_resid name, uint16_t lang) { 280 | TypeMap::iterator iter = this->types.find((resid)type); 281 | if (iter == this->types.end()) 282 | return false; 283 | bool b = iter->second->remove(name, lang); 284 | if (iter->second->isEmpty()) { 285 | free_id(iter->first); 286 | delete iter->second; 287 | this->types.erase(iter); 288 | } 289 | return b; 290 | } 291 | bool Rsrc::add(const_resid type, const_resid name, uint16_t lang, const void* data, size_t size, Overwrite overwrite) { 292 | TypeMap::iterator iter = this->types.find((resid)type); 293 | if (iter == types.end() && (overwrite == ALWAYS || overwrite == NEVER)) { 294 | //types.set(dup(type), new ResourceType(type, name, lang, data, size)); 295 | types[dup(type)] = new ResourceType(type, name, lang, data, size); 296 | return true; 297 | } 298 | return iter->second->add(name, lang, data, size, overwrite); 299 | } 300 | bool Rsrc::isEmpty() const { return this->types.size() == 0; } 301 | ResourceType* Rsrc::operator[](const_resid type) { 302 | TypeMap::iterator iter = this->types.find((resid)type); 303 | return iter == this->types.end() ? NULL : iter->second; 304 | } 305 | const ResourceType* Rsrc::operator[](const_resid type) const { 306 | TypeMap::const_iterator iter = this->types.find((resid)type); 307 | return iter == this->types.end() ? NULL : iter->second; 308 | } 309 | std::vector Rsrc::getTypes() const { 310 | std::vector v; 311 | v.reserve(this->types.size()); 312 | for (TypeMap::const_iterator i = this->types.begin(); i != this->types.end(); ++i) 313 | v.push_back(i->first); 314 | return v; 315 | } 316 | std::vector Rsrc::getNames(const_resid type) const { 317 | TypeMap::const_iterator iter = this->types.find((resid)type); 318 | return iter == types.end() ? std::vector() : iter->second->getNames(); 319 | } 320 | std::vector Rsrc::getLangs(const_resid type, const_resid name) const { 321 | TypeMap::const_iterator iter = this->types.find((resid)type); 322 | return iter == this->types.end() ? std::vector() : iter->second->getLangs(name); 323 | } 324 | size_t Rsrc::getDataSize() const { 325 | size_t size = 0; 326 | for (TypeMap::const_iterator i = this->types.begin(); i != this->types.end(); ++i) { 327 | if (!IsIntResID(i->first)) 328 | size += roundUpTo<4>(sizeof(uint16_t)+wcslen(i->first)*sizeof(wchar_t)); 329 | size += i->second->getDataSize(); 330 | } 331 | return size; 332 | } 333 | size_t Rsrc::getHeaderSize() const { 334 | size_t size = this->getThisHeaderSize(); 335 | for (TypeMap::const_iterator i = this->types.begin(); i != this->types.end(); ++i) 336 | size += i->second->getHeaderSize(); 337 | return size; 338 | } 339 | size_t Rsrc::getThisHeaderSize() const { return sizeof(ResourceDirectory)+this->types.size()*sizeof(ResourceDirectoryEntry); } 340 | void* Rsrc::compile(size_t *size, uint32_t startVA) { 341 | this->cleanup(); 342 | 343 | size_t dataSize = this->getDataSize(); 344 | size_t headerSize = roundUpTo<4>(this->getHeaderSize()); // uint32 alignment 345 | 346 | *size = headerSize + dataSize; 347 | 348 | bytes data = (bytes)memset(malloc(*size), 0, *size); 349 | 350 | size_t pos = 0; 351 | size_t posDir = this->getThisHeaderSize(); 352 | size_t posData = headerSize; 353 | 354 | WriteResDir(data, pos, this->types.begin(), this->types.end()); 355 | 356 | for (TypeMap::const_iterator i = this->types.begin(); i != this->types.end(); ++i) 357 | WriteResDirEntry(data, i->first, i->second->getThisHeaderSize(), pos, posDir, posData); 358 | 359 | for (TypeMap::const_iterator i = this->types.begin(); i != this->types.end(); ++i) 360 | i->second->writeNameDirs(data, pos, posDir, posData); 361 | for (TypeMap::const_iterator i = this->types.begin(); i != this->types.end(); ++i) 362 | i->second->writeLangDirs(data, pos, posDir); 363 | for (TypeMap::const_iterator i = this->types.begin(); i != this->types.end(); ++i) 364 | i->second->writeData(data, pos, posData, startVA); 365 | 366 | return data; 367 | } 368 | size_t Rsrc::getRESSize() const { 369 | size_t size = RESHeaderSize; 370 | for (TypeMap::const_iterator i = this->types.begin(); i != this->types.end(); ++i) 371 | size += i->second->getRESSize(); 372 | return size; 373 | } 374 | void* Rsrc::compileRES(size_t *size) { 375 | this->cleanup(); 376 | 377 | *size = this->getRESSize(); 378 | bytes data = (bytes)memset(malloc(*size), 0, *size); 379 | size_t pos = WriteRESHeader(data, 0, 0, 0, 0); 380 | 381 | for (TypeMap::const_iterator i = this->types.begin(); i != this->types.end(); ++i) 382 | i->second->writeRESData(data, pos); 383 | 384 | return data; 385 | } 386 | #pragma endregion 387 | 388 | #pragma region ResourceType 389 | /////////////////////////////////////////////////////////////////////////////// 390 | ///// ResourceType 391 | /////////////////////////////////////////////////////////////////////////////// 392 | ResourceType::ResourceType(const_resid type, const_bytes data, size_t size, uint32_t start, uint32_t startVA, ResourceDirectoryEntry entry) : type(dup(type)) { 393 | uint32_t nEntries; 394 | ResourceDirectoryEntry *entries = GetEntries(data, size, start+entry.OffsetToDirectory, &nEntries); 395 | for (uint16_t i = 0; i < nEntries; i++) { 396 | str name = GetResourceName(data, size, start, entries[i]); 397 | //this->names.set(name, new ResourceName(name, data, size, start, startVA, entries[i])); 398 | this->names[name] = new ResourceName(name, data, size, start, startVA, entries[i]); 399 | } 400 | } 401 | ResourceType::ResourceType(const_resid type, const_resid name, uint16_t lang, const void* data, size_t size) : type(dup(type)) { 402 | //this->names.set(dup(name), new ResourceName(name, lang, data, size)); 403 | this->names[dup(name)] = new ResourceName(name, lang, data, size); 404 | } 405 | ResourceType::~ResourceType() { 406 | for (NameMap::const_iterator i = this->names.begin(); i != this->names.end(); ++i) { 407 | free_id(i->first); 408 | delete i->second; 409 | } 410 | this->names.clear(); 411 | free_id(type); 412 | } 413 | const_resid ResourceType::getId() const { return this->type; } 414 | bool ResourceType::cleanup() { 415 | for (NameMap::iterator i = this->names.begin(); i != this->names.end(); ) { 416 | if (i->second->cleanup()) { 417 | free_id(i->first); 418 | delete i->second; 419 | this->names.erase(i++); 420 | } else { ++i; } 421 | } 422 | return this->isEmpty(); 423 | } 424 | bool ResourceType::exists(const_resid name, uint16_t lang) const { 425 | NameMap::const_iterator iter = this->names.find((resid)name); 426 | return iter == this->names.end() ? false : iter->second->exists(lang); 427 | } 428 | bool ResourceType::exists(const_resid name, uint16_t *lang) const { 429 | NameMap::const_iterator iter = this->names.find((resid)name); 430 | return iter == this->names.end() ? false : iter->second->exists(lang); 431 | } 432 | void* ResourceType::get(const_resid name, uint16_t lang, size_t *size) const { 433 | NameMap::const_iterator iter = this->names.find((resid)name); 434 | return iter == this->names.end() ? NULL : iter->second->get(lang, size); 435 | } 436 | void* ResourceType::get(const_resid name, uint16_t *lang, size_t *size) const { 437 | NameMap::const_iterator iter = this->names.find((resid)name); 438 | return iter == this->names.end() ? NULL : iter->second->get(lang, size); 439 | } 440 | bool ResourceType::remove(const_resid name, uint16_t lang) { 441 | NameMap::iterator iter = this->names.find((resid)name); 442 | if (iter == this->names.end()) 443 | return false; 444 | bool b = iter->second->remove(lang); 445 | if (iter->second->isEmpty()) { 446 | free_id(iter->first); 447 | delete iter->second; 448 | this->names.erase(iter); 449 | } 450 | return b; 451 | } 452 | bool ResourceType::add(const_resid name, uint16_t lang, const void* data, size_t size, Overwrite overwrite) { 453 | NameMap::iterator iter = this->names.find((resid)name); 454 | if (iter == this->names.end() && (overwrite == ALWAYS || overwrite == NEVER)) { 455 | //this->names.set(dup(name), new ResourceName(name, lang, data, size)); 456 | this->names[dup(name)] = new ResourceName(name, lang, data, size); 457 | return true; 458 | } 459 | return iter->second->add(lang, data, size, overwrite); 460 | } 461 | bool ResourceType::isEmpty() const { return this->names.empty(); } 462 | ResourceName* ResourceType::operator[](const_resid name) { 463 | NameMap::iterator iter = this->names.find((resid)name); 464 | return iter == this->names.end() ? NULL : iter->second; 465 | } 466 | const ResourceName* ResourceType::operator[](const_resid name) const { 467 | NameMap::const_iterator iter = this->names.find((resid)name); 468 | return iter == this->names.end() ? NULL : iter->second; 469 | } 470 | std::vector ResourceType::getNames() const { 471 | std::vector v; 472 | v.reserve(this->names.size()); 473 | for (NameMap::const_iterator i = this->names.begin(); i != this->names.end(); ++i) 474 | v.push_back(i->first); 475 | return v; 476 | } 477 | std::vector ResourceType::getLangs(const_resid name) const { 478 | NameMap::const_iterator iter = this->names.find((resid)name); 479 | return iter == this->names.end() ? std::vector() : iter->second->getLangs(); 480 | } 481 | size_t ResourceType::getDataSize() const { 482 | size_t size = 0; 483 | for (NameMap::const_iterator i = this->names.begin(); i != this->names.end(); ++i) { 484 | if (!IsIntResID(i->first)) 485 | size += roundUpTo<4>(sizeof(uint16_t)+wcslen(i->first)*sizeof(wchar_t)); 486 | size += i->second->getDataSize(); 487 | } 488 | return size; 489 | } 490 | size_t ResourceType::getHeaderSize() const { 491 | size_t size = this->getThisHeaderSize(); 492 | for (NameMap::const_iterator i = this->names.begin(); i != this->names.end(); ++i) 493 | size += i->second->getHeaderSize(); 494 | return size; 495 | } 496 | size_t ResourceType::getThisHeaderSize() const { return sizeof(ResourceDirectory)+this->names.size()*sizeof(ResourceDirectoryEntry); } 497 | void ResourceType::writeNameDirs(bytes data, size_t& pos, size_t& posDir, size_t& posData) const { 498 | WriteResDir(data, pos, this->names.begin(), this->names.end()); 499 | for (NameMap::const_iterator i = this->names.begin(); i != this->names.end(); ++i) 500 | WriteResDirEntry(data, i->first, i->second->getThisHeaderSize(), pos, posDir, posData); 501 | } 502 | void ResourceType::writeLangDirs(bytes data, size_t& pos, size_t& posDir) const { 503 | for (NameMap::const_iterator i = this->names.begin(); i != this->names.end(); ++i) 504 | i->second->writeLangDirs(data, pos, posDir); 505 | } 506 | void ResourceType::writeData(bytes data, size_t& posDataEntry, size_t& posData, uint32_t startVA) const { 507 | for (NameMap::const_iterator i = this->names.begin(); i != this->names.end(); ++i) 508 | i->second->writeData(data, posDataEntry, posData, startVA); 509 | } 510 | size_t ResourceType::getRESSize() const { 511 | size_t xlen = GetRESHeaderIDExtraLen(this->type), size = 0; 512 | for (NameMap::const_iterator i = this->names.begin(); i != this->names.end(); ++i) 513 | size += i->second->getRESSize(xlen); 514 | return size; 515 | } 516 | void ResourceType::writeRESData(bytes data, size_t& pos) const { 517 | for (NameMap::const_iterator i = this->names.begin(); i != this->names.end(); ++i) 518 | i->second->writeRESData(data, pos, this->type); 519 | } 520 | #pragma endregion 521 | 522 | #pragma region ResourceName 523 | /////////////////////////////////////////////////////////////////////////////// 524 | ///// ResourceName 525 | /////////////////////////////////////////////////////////////////////////////// 526 | ResourceName::ResourceName(const_resid name, const_bytes data, size_t size, uint32_t start, uint32_t startVA, ResourceDirectoryEntry entry) : name(dup(name)) { 527 | uint32_t nEntries; 528 | ResourceDirectoryEntry *entries = GetEntries(data, size, start+entry.OffsetToDirectory, &nEntries); 529 | for (uint16_t i = 0; i < nEntries; i++) 530 | //this->langs.set(entries[i].Id, new ResourceLang(entries[i].Id, data, size, start, startVA, entries[i])); 531 | this->langs[entries[i].Id] = new ResourceLang(entries[i].Id, data, size, start, startVA, entries[i]); 532 | } 533 | ResourceName::ResourceName(const_resid name, uint16_t lang, const void* data, size_t size) : name(dup(name)) { 534 | //this->langs.set(lang, new ResourceLang(lang, data, size)); 535 | this->langs[lang] = new ResourceLang(lang, data, size); 536 | } 537 | ResourceName::~ResourceName() { 538 | for (LangMap::const_iterator i = this->langs.begin(); i != this->langs.end(); ++i) { 539 | delete i->second; 540 | } 541 | this->langs.clear(); 542 | free_id(this->name); 543 | } 544 | const_resid ResourceName::getId() const { return this->name; } 545 | bool ResourceName::cleanup() { 546 | for (LangMap::iterator i = this->langs.begin(); i != this->langs.end(); ) { 547 | if (i->second->getDataSize() == 0) { 548 | delete i->second; 549 | this->langs.erase(i++); 550 | } else { ++i; } 551 | } 552 | return this->isEmpty(); 553 | } 554 | bool ResourceName::exists(uint16_t lang) const { return this->langs.find(lang) != this->langs.end(); } 555 | bool ResourceName::exists(uint16_t *lang) const { 556 | if (this->langs.size() > 0) { 557 | if (lang) *lang = this->langs.begin()->first; 558 | return true; 559 | } 560 | return false; 561 | } 562 | void* ResourceName::get(uint16_t lang, size_t *size) const { 563 | LangMap::const_iterator iter = this->langs.find(lang); 564 | return iter == this->langs.end() ? NULL : iter->second->get(size); 565 | } 566 | void* ResourceName::get(uint16_t *lang, size_t *size) const { 567 | if (this->langs.size() > 0) { 568 | LangMap::const_iterator iter = this->langs.begin(); 569 | if (lang) *lang = iter->first; 570 | return iter->second->get(size); 571 | } 572 | return NULL; 573 | } 574 | bool ResourceName::remove(uint16_t lang) { 575 | LangMap::iterator iter = this->langs.find(lang); 576 | if (iter == this->langs.end()) 577 | return false; 578 | delete iter->second; 579 | this->langs.erase(iter); 580 | return true; 581 | } 582 | bool ResourceName::add(uint16_t lang, const void* data, size_t size, Overwrite overwrite) { 583 | LangMap::iterator iter = this->langs.find(lang); 584 | if (iter == this->langs.end() && (overwrite == ALWAYS || overwrite == NEVER)) { 585 | //this->langs.set(lang, new ResourceLang(lang, data, size)); 586 | this->langs[lang] = new ResourceLang(lang, data, size); 587 | return true; 588 | } else if (overwrite == ALWAYS || overwrite == ONLY) { 589 | return iter->second->set(data, size); 590 | } 591 | return false; 592 | } 593 | bool ResourceName::isEmpty() const { return this->langs.empty(); } 594 | ResourceLang* ResourceName::operator[](uint16_t lang) { 595 | LangMap::iterator iter = this->langs.find(lang); 596 | return iter == this->langs.end() ? NULL : iter->second; 597 | } 598 | const ResourceLang* ResourceName::operator[](uint16_t lang) const { 599 | LangMap::const_iterator iter = this->langs.find(lang); 600 | return iter == this->langs.end() ? NULL : iter->second; 601 | } 602 | std::vector ResourceName::getLangs() const { 603 | std::vector v; 604 | v.reserve(this->langs.size()); 605 | for (LangMap::const_iterator i = this->langs.begin(); i != this->langs.end(); ++i) 606 | v.push_back(i->first); 607 | return v; 608 | } 609 | size_t ResourceName::getDataSize() const { 610 | size_t size = 0; 611 | for (LangMap::const_iterator i = this->langs.begin(); i != this->langs.end(); ++i) 612 | size += roundUpTo<4>(i->second->getDataSize()); 613 | return size; 614 | } 615 | size_t ResourceName::getHeaderSize() const { 616 | size_t size = this->getThisHeaderSize(); 617 | for (LangMap::const_iterator i = this->langs.begin(); i != this->langs.end(); ++i) 618 | size += i->second->getHeaderSize(); 619 | return size; 620 | } 621 | size_t ResourceName::getThisHeaderSize() const { return sizeof(ResourceDirectory)+this->langs.size()*sizeof(ResourceDirectoryEntry); } 622 | void ResourceName::writeLangDirs(bytes data, size_t& pos, size_t& posDir) const { 623 | WriteResDir(data, pos, 0, (uint16_t)this->langs.size()); 624 | for (LangMap::const_iterator i = this->langs.begin(); i != this->langs.end(); ++i) { 625 | ResourceDirectoryEntry entry; 626 | entry.DataIsDirectory = 0; 627 | entry.OffsetToDirectory = posDir; 628 | entry.NameIsString = 0; 629 | entry.Name = i->first; 630 | 631 | memcpy(data+pos, &entry, sizeof(ResourceDirectoryEntry)); 632 | 633 | posDir += i->second->getThisHeaderSize(); 634 | pos += sizeof(ResourceDirectoryEntry); 635 | } 636 | } 637 | void ResourceName::writeData(bytes data, size_t& posDataEntry, size_t& posData, uint32_t startVA) const { 638 | for (LangMap::const_iterator i = this->langs.begin(); i != this->langs.end(); ++i) 639 | i->second->writeData(data, posDataEntry, posData, startVA); 640 | } 641 | size_t ResourceName::getRESSize(size_t addl_hdr_size) const { 642 | size_t xlen = addl_hdr_size + GetRESHeaderIDExtraLen(this->name), size = 0; 643 | for (LangMap::const_iterator i = this->langs.begin(); i != this->langs.end(); ++i) 644 | size += i->second->getRESSize(xlen); 645 | return size; 646 | } 647 | void ResourceName::writeRESData(bytes data, size_t& pos, const_resid type) const { 648 | for (LangMap::const_iterator i = this->langs.begin(); i != this->langs.end(); ++i) 649 | i->second->writeRESData(data, pos, type, this->name); 650 | } 651 | 652 | #pragma endregion 653 | 654 | #pragma region ResourceLang 655 | /////////////////////////////////////////////////////////////////////////////// 656 | ///// ResourceLang 657 | /////////////////////////////////////////////////////////////////////////////// 658 | ResourceLang::ResourceLang(uint16_t lang, const_bytes data, size_t size, uint32_t start, uint32_t startVA, ResourceDirectoryEntry entry) : lang(lang) { 659 | if (start+entry.OffsetToData+sizeof(ResourceDataEntry) > size) { throw resLoadFailure; } 660 | ResourceDataEntry de = *(ResourceDataEntry*)(data+start+entry.OffsetToData); 661 | if (start+de.OffsetToData-startVA+de.Size > size) { throw resLoadFailure; } 662 | this->data = malloc(this->length = de.Size); 663 | if (this->length == 0) { return; } 664 | memcpy(this->data, data+start+de.OffsetToData-startVA, this->length); 665 | } 666 | ResourceLang::ResourceLang(uint16_t lang, const void* data, size_t size) : lang(lang), length(size) { 667 | this->data = memcpy(malloc(size), data, length); 668 | } 669 | ResourceLang::~ResourceLang() { free(this->data); } 670 | const_resid ResourceLang::getId() const { return MakeResID(this->lang); } 671 | void* ResourceLang::get(size_t *size) const { return memcpy(malloc(this->length), this->data, *size = this->length); } 672 | bool ResourceLang::set(const void* dat, size_t size) { 673 | if (this->length != size) 674 | { 675 | free(this->data); 676 | this->data = malloc(this->length = size); 677 | } 678 | memcpy(this->data, dat, size); 679 | return true; 680 | } 681 | size_t ResourceLang::getDataSize() const { return this->length; } 682 | size_t ResourceLang::getHeaderSize() const { return sizeof(ResourceDataEntry); } 683 | size_t ResourceLang::getThisHeaderSize() const { return sizeof(ResourceDataEntry); } 684 | void ResourceLang::writeData(bytes dat, size_t& posDataEntry, size_t& posData, size_t startVA) const { 685 | ResourceDataEntry de = {(uint32_t)(posData+startVA), (uint32_t)this->length, 0, 0}; // needs to be an RVA 686 | memcpy(dat+posDataEntry, &de, sizeof(ResourceDataEntry)); 687 | posDataEntry += sizeof(ResourceDataEntry); 688 | memcpy(dat+posData, this->data, this->length); 689 | posData += roundUpTo<4>(this->length); 690 | } 691 | size_t ResourceLang::getRESSize(size_t addl_hdr_size) const { return roundUpTo<4>(this->length + RESHeaderSize + addl_hdr_size); } 692 | void ResourceLang::writeRESData(bytes dat, size_t& pos, const_resid type, const_resid name) const { 693 | pos += WriteRESHeader(dat+pos, type, name, this->lang, this->length); 694 | memcpy(dat+pos, this->data, this->length); 695 | pos = roundUpTo<4>(pos + this->length); 696 | } 697 | #pragma endregion 698 | -------------------------------------------------------------------------------- /PEFileResources.h: -------------------------------------------------------------------------------- 1 | // pe-file: library for reading and manipulating pe-files 2 | // Copyright (C) 2012 Jeffrey Bush jeff@coderforlife.com 3 | // 4 | // This library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | // Implements the classes and functions for dealing with resources in PE files or RES files 18 | 19 | #ifndef PE_FILE_RESOURCES_H 20 | #define PE_FILE_RESOURCES_H 21 | 22 | #ifdef EXPOSE_DIRECT_RESOURCES 23 | #define _DECLARE_ALL_PE_FILE_RESOURCES_ 24 | #endif 25 | 26 | #ifndef _DECLARE_ALL_PE_FILE_RESOURCES_ 27 | 28 | class Rsrc; 29 | 30 | #else 31 | 32 | #include "PEDataTypes.h" 33 | 34 | #include 35 | #include 36 | 37 | namespace PE { 38 | 39 | // A comparator for resource names 40 | struct ResCmp { bool operator()(const_resid a, const_resid b) const; }; 41 | 42 | // A resource (directory) entry 43 | class Resource { 44 | public: 45 | virtual const_resid getId() const = 0; 46 | private: 47 | virtual size_t getDataSize() const = 0; 48 | virtual size_t getHeaderSize() const = 0; 49 | virtual size_t getThisHeaderSize() const = 0; 50 | }; 51 | 52 | // The final resource directory, contains the data for the resource 53 | class ResourceLang : Resource { 54 | friend class ResourceName; 55 | 56 | uint16_t lang; 57 | void* data; 58 | size_t length; 59 | 60 | ResourceLang(uint16_t lang, const_bytes data, size_t size, uint32_t start, uint32_t startVA, Image::ResourceDirectoryEntry entry); 61 | ResourceLang(uint16_t lang, const void* data, size_t size); 62 | public: 63 | ~ResourceLang(); 64 | 65 | const_resid getId() const; 66 | 67 | bool isLoaded() const; 68 | void* get(size_t* size) const; // must be freed 69 | bool set(const void* data, size_t size); 70 | 71 | private: 72 | virtual size_t getDataSize() const; 73 | virtual size_t getHeaderSize() const; 74 | virtual size_t getThisHeaderSize() const; 75 | void writeData(bytes data, size_t& posDataEntry, size_t& posData, size_t startVA) const; 76 | 77 | virtual size_t getRESSize(size_t addl_hdr_size) const; 78 | void writeRESData(bytes data, size_t& pos, const_resid type, const_resid name) const; 79 | }; 80 | 81 | // The named resource directory, the second level 82 | class ResourceName : Resource { 83 | friend class ResourceType; 84 | 85 | resid name; 86 | 87 | typedef std::map LangMap; 88 | LangMap langs; 89 | 90 | ResourceName(const_resid name, const_bytes data, size_t size, uint32_t start, uint32_t startVA, Image::ResourceDirectoryEntry entry); 91 | ResourceName(const_resid name, uint16_t lang, const void* data, size_t size); 92 | public: 93 | ~ResourceName(); 94 | 95 | const_resid getId() const; 96 | 97 | bool exists(uint16_t lang) const; 98 | bool exists(uint16_t* lang) const; 99 | void* get(uint16_t lang, size_t* size) const; 100 | void* get(uint16_t* lang, size_t* size) const; 101 | bool remove(uint16_t lang); 102 | bool add(uint16_t lang, const void* data, size_t size, Overwrite overwrite = ALWAYS); 103 | 104 | bool isEmpty() const; 105 | 106 | ResourceLang* operator[](uint16_t lang); 107 | const ResourceLang* operator[](uint16_t lang) const; 108 | 109 | std::vector getLangs() const; 110 | 111 | private: 112 | bool cleanup(); 113 | virtual size_t getDataSize() const; 114 | virtual size_t getHeaderSize() const; 115 | virtual size_t getThisHeaderSize() const; 116 | void writeLangDirs(bytes data, size_t& pos, size_t& posDir) const; 117 | void writeData(bytes data, size_t& posDataEntry, size_t& posData, uint32_t startVA) const; 118 | 119 | virtual size_t getRESSize(size_t addl_hdr_size) const; 120 | void writeRESData(bytes data, size_t& pos, const_resid type) const; 121 | }; 122 | 123 | // The typed resource directory, the first level 124 | class ResourceType : Resource { 125 | friend class Rsrc; 126 | 127 | resid type; 128 | typedef std::map NameMap; 129 | NameMap names; 130 | 131 | ResourceType(const_resid type, const_bytes data, size_t size, uint32_t start, uint32_t startVA, Image::ResourceDirectoryEntry entry); 132 | ResourceType(const_resid type, const_resid name, uint16_t lang, const void* data, size_t size); 133 | public: 134 | ~ResourceType(); 135 | 136 | const_resid getId() const; 137 | 138 | bool exists(const_resid name, uint16_t lang) const; 139 | bool exists(const_resid name, uint16_t* lang) const; 140 | void* get (const_resid name, uint16_t lang, size_t* size) const; 141 | void* get (const_resid name, uint16_t* lang, size_t* size) const; 142 | bool remove(const_resid name, uint16_t lang); 143 | bool add(const_resid name, uint16_t lang, const void* data, size_t size, Overwrite overwrite = ALWAYS); 144 | 145 | bool isEmpty() const; 146 | 147 | ResourceName* operator[](const_resid name); 148 | const ResourceName* operator[](const_resid name) const; 149 | 150 | std::vector getNames() const; 151 | std::vector getLangs(const_resid name) const; 152 | 153 | private: 154 | bool cleanup(); 155 | virtual size_t getDataSize() const; 156 | virtual size_t getHeaderSize() const; 157 | virtual size_t getThisHeaderSize() const; 158 | void writeNameDirs(bytes data, size_t& pos, size_t& posDir, size_t& posData) const; 159 | void writeLangDirs(bytes data, size_t& pos, size_t& posDir) const; 160 | void writeData(bytes data, size_t& posDataEntry, size_t& posData, uint32_t startVA) const; 161 | 162 | virtual size_t getRESSize() const; 163 | void writeRESData(bytes data, size_t& pos) const; 164 | }; 165 | 166 | class Rsrc : Resource { 167 | typedef std::map TypeMap; 168 | TypeMap types; 169 | 170 | Rsrc(const_bytes data, size_t size, Image::SectionHeader *section); // creates from ".rsrc" section in PE file 171 | Rsrc(const_bytes data, size_t size); // creates from RES file 172 | Rsrc(); // creates empty 173 | public: 174 | ~Rsrc(); 175 | 176 | static Rsrc* createFromRSRCSection(const_bytes data, size_t size, Image::SectionHeader *section); 177 | static Rsrc* createFromRESFile(const_bytes data, size_t size); 178 | static Rsrc* createEmpty(); 179 | 180 | const_resid getId() const; 181 | 182 | bool exists(const_resid type, const_resid name, uint16_t lang) const; 183 | bool exists(const_resid type, const_resid name, uint16_t* lang) const; 184 | void* get (const_resid type, const_resid name, uint16_t lang, size_t* size) const; 185 | void* get (const_resid type, const_resid name, uint16_t* lang, size_t* size) const; 186 | bool remove(const_resid type, const_resid name, uint16_t lang); 187 | bool add(const_resid type, const_resid name, uint16_t lang, const void* data, size_t size, Overwrite overwrite = ALWAYS); 188 | 189 | bool isEmpty() const; 190 | 191 | ResourceType* operator[](const_resid type); 192 | const ResourceType* operator[](const_resid type) const; 193 | 194 | std::vector getTypes() const; 195 | std::vector getNames(const_resid type) const; 196 | std::vector getLangs(const_resid type, const_resid name) const; 197 | 198 | bool cleanup(); 199 | void* compile(size_t* size, uint32_t startVA); // calls cleanup 200 | void* compileRES(size_t* size); // calls cleanup 201 | 202 | private: 203 | virtual size_t getDataSize() const; 204 | virtual size_t getHeaderSize() const; 205 | virtual size_t getThisHeaderSize() const; 206 | virtual size_t getRESSize() const; 207 | }; 208 | 209 | } 210 | 211 | #endif 212 | #endif -------------------------------------------------------------------------------- /PEVersion.cpp: -------------------------------------------------------------------------------- 1 | // pe-file: library for reading and manipulating pe-files 2 | // Copyright (C) 2012 Jeffrey Bush jeff@coderforlife.com 3 | // 4 | // This library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | #ifdef __cplusplus_cli 18 | #pragma unmanaged 19 | #endif 20 | 21 | #include "PEVersion.h" 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | using namespace PE; 28 | using namespace PE::Internal; 29 | using namespace PE::Version; 30 | 31 | PE::Version::Version::Version() : Minor(0), Major(0), Revision(0), Build(0) { } 32 | SmallVersion::SmallVersion() : Minor(0), Major(0) { } 33 | LangAndCodePage::LangAndCodePage() : Language(0), CodePage(0) { } 34 | bool LangAndCodePage::operator <(const LangAndCodePage& other) const { 35 | return (this->Language == other.Language) ? this->CodePage < other.CodePage : this->Language < other.Language; 36 | } 37 | 38 | struct Block16 { 39 | uint16_t size; // size including key, value, and children 40 | uint16_t val_size; 41 | char* key; 42 | bytes value; 43 | std::vector children; 44 | }; 45 | typedef std::vector::iterator B16iter; 46 | 47 | struct Block32 { 48 | uint16_t size; // size including key, value, and children 49 | uint16_t val_size; 50 | uint16_t type; // 0x0000 for a binary value, 0x0001 for a string value 51 | wchar_t* key; 52 | bytes value; 53 | std::vector children; 54 | }; 55 | typedef std::vector::iterator B32iter; 56 | 57 | static Block16 GetBlock16(const void* ver, bool recurse) { 58 | uint16_t* words = (uint16_t*)ver; 59 | 60 | Block16 b = { words[0], words[1], (char*)(words+2) }; 61 | b.value = ((bytes)ver) + roundUpTo(2 * sizeof(uint16_t) + (strlen(b.key) + 1) * sizeof(char)); 62 | 63 | //if (b.key[0] < ' ') { /* error: actually a 32-bit block*/ } 64 | 65 | if (recurse) { 66 | bytes end = (bytes)ver+b.size; 67 | bytes child = b.value + roundUpTo(b.val_size); 68 | while (child < end) { 69 | Block16 c = GetBlock16(child, true); 70 | b.children.push_back(c); 71 | child += c.size; 72 | } 73 | } 74 | 75 | return b; 76 | } 77 | 78 | static Block32 GetBlock32(const void* ver, bool recurse) { 79 | uint16_t* words = (uint16_t*)ver; 80 | 81 | Block32 b = { words[0], words[1], words[2], (wchar_t*)(words+3) }; 82 | b.value = ((bytes)ver) + roundUpTo(3 * sizeof(uint16_t) + (wcslen(b.key) + 1) * sizeof(wchar_t)); 83 | 84 | //if (*((char*)&b.type) >= ' ') { /* error: actually a 16-bit block*/ } 85 | 86 | if (recurse) { 87 | bytes end = (bytes)ver+b.size; 88 | bytes child = b.value + roundUpTo(b.val_size); 89 | while (child < end) { 90 | Block32 c = GetBlock32(child, true); 91 | b.children.push_back(c); 92 | child += c.size; 93 | } 94 | } 95 | 96 | return b; 97 | } 98 | 99 | static FileVersionBasicInfo *GetFileVersionBasicInfo(Block32 root) { 100 | if (wcscmp(root.key, L"VS_VERSION_INFO") != 0 || root.type != 0x0000 || root.val_size != 52) { return NULL; } // error! 101 | FileVersionBasicInfo *v = (FileVersionBasicInfo*)root.value; 102 | if (v->Signature != FileVersionBasicInfo::SIGNATURE || v->StrucVersion.Major != 1 || v->StrucVersion.Minor != 0) { return NULL; } // error! 103 | return v; 104 | } 105 | FileVersionBasicInfo *FileVersionBasicInfo::Get(void* ver) { return ver ? GetFileVersionBasicInfo(GetBlock32(ver, false)) : NULL; } 106 | 107 | FileVersionInfo::FileVersionInfo(void* ver) : Basic(NULL) { 108 | if (ver == NULL) { return; } 109 | Block32 root = GetBlock32(ver, true); 110 | this->Basic = GetFileVersionBasicInfo(root); 111 | if (this->Basic == NULL) { return; } // error! 112 | 113 | for (B32iter i = root.children.begin(); i != root.children.end(); ++i) { 114 | if (wcscmp(i->key, L"StringFileInfo") == 0) { 115 | for (B32iter j = i->children.begin(); j != i->children.end(); ++j) { 116 | if (j->val_size == 0) { 117 | LangAndCodePage lcp; // TODO: convert j->key to LCP 118 | StringFileInfo& sfi = this->Strings[lcp]; 119 | for (B32iter k = j->children.begin(); k != j->children.end(); ++k) { 120 | sfi[k->key] = std::wstring((wchar_t*)k->value, k->val_size / sizeof(wchar_t)); 121 | } 122 | } 123 | } 124 | } else if (wcscmp(i->key, L"VarFileInfo") == 0) { 125 | for (B32iter j = i->children.begin(); j != i->children.end(); ++j) { 126 | if (wcscmp(j->key, L"Translation") == 0 && j->val_size == sizeof(LangAndCodePage)) { 127 | this->Strings.insert(make_pair(*(LangAndCodePage*)j->value, FileVersionInfo::StringFileInfo())); 128 | } 129 | } 130 | } else { /* Ignore the unknown block */ } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /PEVersion.h: -------------------------------------------------------------------------------- 1 | // pe-file: library for reading and manipulating pe-files 2 | // Copyright (C) 2012 Jeffrey Bush jeff@coderforlife.com 3 | // 4 | // This library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | // Implements a few utility functions for dealing with version information in PE files 18 | 19 | #ifndef PE_VERSION_H 20 | #define PE_VERSION_H 21 | 22 | #include "PEDataTypes.h" 23 | 24 | #include 25 | #include 26 | 27 | namespace PE { namespace Version { 28 | struct Version { 29 | Version(); 30 | uint16_t Minor, Major, Revision, Build; 31 | }; 32 | struct SmallVersion { 33 | SmallVersion(); 34 | uint16_t Minor, Major; 35 | }; 36 | struct LangAndCodePage { 37 | LangAndCodePage(); 38 | uint16_t Language, CodePage; 39 | bool operator <(const LangAndCodePage& other) const; 40 | }; 41 | 42 | // This data is modifiable, changes in ver show up here and changes here show up in ver 43 | struct FileVersionBasicInfo { 44 | static const uint32_t SIGNATURE = 0xFEEF04BD; 45 | 46 | static FileVersionBasicInfo* Get(void* ver); 47 | 48 | uint32_t Signature; 49 | SmallVersion StrucVersion; 50 | Version FileVersion; 51 | Version ProductVersion; 52 | 53 | typedef enum _FileFlags : uint32_t { // FLAGS 54 | DEBUG = 0x00000001, 55 | PRERELEASE = 0x00000002, 56 | PATCHED = 0x00000004, 57 | PRIVATEBUILD = 0x00000008, 58 | INFOINFERRED = 0x00000010, 59 | SPECIALBUILD = 0x00000020, 60 | } Flags; 61 | Flags FileFlagsMask; 62 | Flags FileFlags; 63 | 64 | typedef enum _OS : uint32_t { // FLAGS, kinda 65 | UNKNOWN_OS = 0x00000000, 66 | 67 | DOS = 0x00010000, 68 | OS216 = 0x00020000, 69 | OS232 = 0x00030000, 70 | NT = 0x00040000, 71 | 72 | WINDOWS16 = 0x00000001, 73 | PM16 = 0x00000002, 74 | PM32 = 0x00000003, 75 | WINDOWS32 = 0x00000004, 76 | 77 | DOS_WINDOWS16 = 0x00010001, 78 | DOS_WINDOWS32 = 0x00010004, 79 | OS216_PM16 = 0x00020002, 80 | OS232_PM32 = 0x00030003, 81 | NT_WINDOWS32 = 0x00040004, 82 | } OS; 83 | OS FileOS; 84 | 85 | typedef enum _Type : uint32_t { 86 | UNKNOWN_TYPE = 0x00000000, 87 | APP = 0x00000001, 88 | DLL = 0x00000002, 89 | DRV = 0x00000003, 90 | FONT = 0x00000004, 91 | VXD = 0x00000005, 92 | STATIC_LIB = 0x00000007, 93 | } Type; 94 | Type FileType; 95 | 96 | typedef enum _SubType : uint32_t { 97 | UNKNOWN_SUB_TYPE = 0x00000000, 98 | 99 | DRV_PRINTER = 0x00000001, 100 | DRV_KEYBOARD = 0x00000002, 101 | DRV_LANGUAGE = 0x00000003, 102 | DRV_DISPLAY = 0x00000004, 103 | DRV_MOUSE = 0x00000005, 104 | DRV_NETWORK = 0x00000006, 105 | DRV_SYSTEM = 0x00000007, 106 | DRV_INSTALLABLE = 0x00000008, 107 | DRV_SOUND = 0x00000009, 108 | DRV_COMM = 0x0000000A, 109 | DRV_VERSIONED_PRINTER = 0x0000000C, 110 | 111 | FONT_RASTER = 0x00000001, 112 | FONT_VECTOR = 0x00000002, 113 | FONT_TRUETYPE = 0x00000003, 114 | } SubType; 115 | SubType FileSubtype; 116 | 117 | uint64_t FileDate; 118 | }; 119 | 120 | // This data is not modifiable (except the basic info), all strings are copies 121 | struct FileVersionInfo { 122 | FileVersionInfo(void* ver); 123 | 124 | FileVersionBasicInfo* Basic; 125 | 126 | typedef std::map StringFileInfo; 127 | typedef std::map StringFileInfos; 128 | StringFileInfos Strings; 129 | }; 130 | } } 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pe-file 2 | ======= 3 | 4 | Basic utility code for reading and modifying PE files (EXE, DLL, ...) 5 | 6 | This is simply code and not a program. It could easily be compiled into a DLL and expose the PEFile class. 7 | 8 | When compiling commenting out EXPOSE_DIRECT_RESOURCES causes direct resource access to be completely blocked and not expose the underlying resource classes. However resources are still accessible though other functions. 9 | 10 | The interface is as follows: 11 | 12 | class PE::File { 13 | public: 14 | File(LPVOID data, size_t size, bool readonly = false); // data is freed when the PEFile is deleted 15 | File(LPCWSTR filename, bool readonly = false); 16 | ~File(); 17 | bool isLoaded() const; 18 | bool isReadOnly() const; 19 | 20 | bool save(); // flushes 21 | 22 | bool is32bit() const; 23 | bool is64bit() const; 24 | ULONGLONG getImageBase() const; 25 | 26 | IMAGE_FILE_HEADER *getFileHeader(); // pointer can modify the file 27 | IMAGE_NT_HEADERS32 *getNtHeaders32(); // pointer can modify the file 28 | IMAGE_NT_HEADERS64 *getNtHeaders64(); // pointer can modify the file 29 | const IMAGE_FILE_HEADER *getFileHeader() const; 30 | const IMAGE_NT_HEADERS32 *getNtHeaders32() const; 31 | const IMAGE_NT_HEADERS64 *getNtHeaders64() const; 32 | 33 | DWORD getDataDirectoryCount() const; 34 | IMAGE_DATA_DIRECTORY *getDataDirectory(int i); // pointer can modify the file 35 | const IMAGE_DATA_DIRECTORY *getDataDirectory(int i) const; 36 | 37 | IMAGE_SECTION_HEADER *getSectionHeader(int i); // pointer can modify the file 38 | IMAGE_SECTION_HEADER *getSectionHeader(const char *str, int *i = NULL); // pointer can modify the file 39 | IMAGE_SECTION_HEADER *getSectionHeaderByRVA(DWORD rva, int *i); // pointer can modify the file 40 | IMAGE_SECTION_HEADER *getSectionHeaderByVA(ULONGLONG va, int *i); // pointer can modify the file 41 | const IMAGE_SECTION_HEADER *getSectionHeader(int i) const; 42 | const IMAGE_SECTION_HEADER *getSectionHeader(const char *str, int *i = NULL) const; 43 | const IMAGE_SECTION_HEADER *getSectionHeaderByRVA(DWORD rva, int *i) const; 44 | const IMAGE_SECTION_HEADER *getSectionHeaderByVA(ULONGLONG va, int *i) const; 45 | int getSectionHeaderCount() const; 46 | 47 | IMAGE_SECTION_HEADER *getExpandedSectionHdr(int i, DWORD room); // pointer can modify the file, invalidates all pointers returned by functions, flushes 48 | IMAGE_SECTION_HEADER *getExpandedSectionHdr(char *str, DWORD room); // as above 49 | 50 | #define CHARS_CODE_SECTION IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ 51 | #define CHARS_INIT_DATA_SECTION_R IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ 52 | #define CHARS_INIT_DATA_SECTION_RW IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE 53 | 54 | IMAGE_SECTION_HEADER *createSection(int i, const char *name, DWORD room, DWORD chars); // pointer can modify the file, invalidates all pointers returned by functions, flushes 55 | IMAGE_SECTION_HEADER *createSection(const char *str, const char *name, DWORD room, DWORD chars); // as above, adds before the section named str 56 | IMAGE_SECTION_HEADER *createSection(const char *name, DWORD room, DWORD chars); // as above, adds before ".reloc" if exists or at the very end 57 | 58 | size_t getSize() const; 59 | bool setSize(size_t dwSize, bool grow_only = true); // invalidates all pointers returned by functions, flushes 60 | 61 | LPBYTE get(DWORD dwOffset = 0, DWORD *dwSize = NULL); // pointer can modify the file 62 | const LPBYTE get(DWORD dwOffset = 0, DWORD *dwSize = NULL) const; 63 | bool set(const LPVOID lpBuffer, DWORD dwSize, DWORD dwOffset); // shorthand for memcpy(f->get(dwOffset), lpBuffer, dwSize) with bounds checking 64 | bool zero(DWORD dwSize, DWORD dwOffset); // shorthand for memset(f->get(dwOffset), 0, dwSize) with bounds checking 65 | bool move(DWORD dwOffset, DWORD dwSize, int dwDistanceToMove); // shorthand for x = f->get(dwOffset); memmove(x+dwDistanceToMove, x, dwSize) with bounds checking 66 | bool shift(DWORD dwOffset, int dwDistanceToMove); // shorthand for f->move(dwOffset, f->getSize() - dwOffset - dwDistanceToMove, dwDistanceToMove) 67 | bool flush(); 68 | 69 | bool updatePEChkSum(); // flushes 70 | bool hasExtraData() const; 71 | LPVOID getExtraData(DWORD *size); // pointer can modify the file, when first enabling it will flush 72 | bool clearCertificateTable(); // may invalidate all pointers returned by functions, flushes 73 | ULONGLONG getFileVersion() const; 74 | bool isAlreadyModified() const; 75 | bool setModifiedFlag(); // flushes 76 | bool removeRelocs(DWORD start, DWORD end, bool reverse = false); 77 | 78 | #ifdef EXPOSE_DIRECT_RESOURCES 79 | Rsrc *getResources(); 80 | const Rsrc *getResources() const; 81 | #endif 82 | bool resourceExists(LPCWSTR type, LPCWSTR name, WORD lang) const; 83 | bool resourceExists(LPCWSTR type, LPCWSTR name, WORD* lang) const; 84 | LPVOID getResource (LPCWSTR type, LPCWSTR name, WORD lang, size_t* size) const; // must be freed 85 | LPVOID getResource (LPCWSTR type, LPCWSTR name, WORD* lang, size_t* size) const; // must be freed 86 | bool removeResource(LPCWSTR type, LPCWSTR name, WORD lang); 87 | bool addResource (LPCWSTR type, LPCWSTR name, WORD lang, const LPVOID data, size_t size, DWORD overwrite = OVERWRITE_ALWAYS); 88 | 89 | static const LPVOID GetResourceDirect(const LPVOID data, LPCWSTR type, LPCWSTR name); // must be freed, massively performance enhanced for a single retrieval and no editing // lang? size? 90 | static bool UpdatePEChkSum(LPBYTE data, size_t dwSize, size_t peOffset, DWORD dwOldCheck); 91 | static bool GetVersionInfo(const LPVOID ver, LPCWSTR query, LPVOID *buffer, PUINT len); 92 | static VS_FIXEDFILEINFO *GetVersionInfo(const LPVOID ver); 93 | static void UnmapAllViewsOfFile(LPCWSTR file); 94 | }; -------------------------------------------------------------------------------- /build-mingw.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | :: This builds using MinGW-w64 for 32 and 64 bit (http://mingw-w64.sourceforge.net/) 4 | :: Make sure both mingw-w32\bin and mingw-w64\bin are in the PATH 5 | 6 | :: -s 7 | set FLAGS=-Wall -Wno-unknown-pragmas -static-libgcc -static-libstdc++ -O3 -D UNICODE -D _UNICODE 8 | set FILES=PEFile.cpp PEFileResources.cpp PEDataSource.cpp PEVersion.cpp 9 | 10 | echo Compiling 32-bit... 11 | i686-w64-mingw32-g++ %FLAGS% -c %FILES% 12 | i686-w64-mingw32-ar rcs libPEFile.a *.o 13 | del /F /Q *.o >NUL 2>&1 14 | echo. 15 | 16 | echo Compiling 64-bit... 17 | x86_64-w64-mingw32-g++ %FLAGS% -c %FILES% 18 | x86_64-w64-mingw32-ar rcs libPEFile64.a *.o 19 | del /F /Q *.o >NUL 2>&1 20 | pause 21 | -------------------------------------------------------------------------------- /build-msvc-debug.bat: -------------------------------------------------------------------------------- 1 | :: This builds using MS Visual C++ with debug symbols 2 | :: Looks if one of %VS*COMNTOOLS% variables exists where * is one of 140 (2015), 3 | :: 120 (2013), 110 (2012), 100 (2010), 90 (2008). They are tried in that order. 4 | 5 | @set DIR= 6 | @if not "%VS90COMNTOOLS%"=="" set DIR=%VS90COMNTOOLS% 7 | @if not "%VS100COMNTOOLS%"=="" set DIR=%VS100COMNTOOLS% 8 | @if not "%VS110COMNTOOLS%"=="" set DIR=%VS110COMNTOOLS% 9 | @if not "%VS120COMNTOOLS%"=="" set DIR=%VS120COMNTOOLS% 10 | @if not "%VS140COMNTOOLS%"=="" set DIR=%VS140COMNTOOLS% 11 | @if "%DIR%"=="" ( 12 | echo Could not find a Visual Studio toolkit 13 | pause 14 | goto :EOF 15 | ) 16 | 17 | @echo Compiling with toolchain at "%DIR%" [DEBUG] 18 | 19 | @set FLAGS=/nologo /MDd /MP /D _DEBUG /Zi /W4 /wd4201 /wd4480 /O2 /GS /EHa /D _UNICODE /D UNICODE 20 | @set FILES=PEFile.cpp PEFileResources.cpp PEDataSource.cpp PEVersion.cpp 21 | 22 | @echo Compiling 32-bit... 23 | @call "%DIR%\..\..\VC\vcvarsall.bat" x86 24 | cl %FLAGS% /FdPEFile_d.pdb %FILES% 25 | lib /nologo /out:PEFile_d.lib *.obj 26 | @del /F /Q *.obj >NUL 2>&1 27 | @echo. 28 | 29 | @echo Compiling 64-bit... 30 | @call "%DIR%\..\..\VC\vcvarsall.bat" x64 31 | cl %FLAGS% /FdPEFile64_d.pdb /c %FILES% 32 | lib /nologo /out:PEFile64_d.lib *.obj 33 | @del /F /Q *.obj >NUL 2>&1 34 | @pause 35 | -------------------------------------------------------------------------------- /build-msvc.bat: -------------------------------------------------------------------------------- 1 | :: This builds using MS Visual C++ 2 | :: Looks if one of %VS*COMNTOOLS% variables exists where * is one of 140 (2015), 3 | :: 120 (2013), 110 (2012), 100 (2010), 90 (2008). They are tried in that order. 4 | 5 | @set DIR= 6 | @if not "%VS90COMNTOOLS%"=="" set DIR=%VS90COMNTOOLS% 7 | @if not "%VS100COMNTOOLS%"=="" set DIR=%VS100COMNTOOLS% 8 | @if not "%VS110COMNTOOLS%"=="" set DIR=%VS110COMNTOOLS% 9 | @if not "%VS120COMNTOOLS%"=="" set DIR=%VS120COMNTOOLS% 10 | @if not "%VS140COMNTOOLS%"=="" set DIR=%VS140COMNTOOLS% 11 | @if "%DIR%"=="" ( 12 | echo Could not find a Visual Studio toolkit 13 | pause 14 | goto :EOF 15 | ) 16 | 17 | @echo Compiling with toolchain at "%DIR%" 18 | 19 | @set FLAGS=/nologo /MT /MP /D NDEBUG /W4 /wd4201 /wd4480 /O2 /GS /GL /EHa /D _UNICODE /D UNICODE 20 | @set FILES=PEFile.cpp PEFileResources.cpp PEDataSource.cpp PEVersion.cpp 21 | 22 | @echo Compiling 32-bit... 23 | @call "%DIR%\..\..\VC\vcvarsall.bat" x86 24 | cl %FLAGS% /c %FILES% 25 | lib /nologo /ltcg /out:PEFile.lib *.obj 26 | @del /F /Q *.obj >NUL 2>&1 27 | @echo. 28 | 29 | @echo Compiling 64-bit... 30 | @call "%DIR%\..\..\VC\vcvarsall.bat" x64 31 | cl %FLAGS% /c %FILES% 32 | lib /nologo /ltcg /out:PEFile64.lib *.obj 33 | @del /F /Q *.obj >NUL 2>&1 34 | @pause 35 | -------------------------------------------------------------------------------- /gpl.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------