aboutsummaryrefslogtreecommitdiff
path: root/core/osutils.cc
diff options
context:
space:
mode:
authorjmpoep <OriginalEntryPoint@qq.com>2023-12-07 16:51:07 +0800
committerjmpoep <OriginalEntryPoint@qq.com>2023-12-07 16:51:07 +0800
commit28008a746a31abb7909dd86cb0cd413ac8943b0b (patch)
treea30b74b8cad548048c3c1551d652828ab76fa9bd /core/osutils.cc
downloadvmprotect-3.5.1-master.tar
vmprotect-3.5.1-master.tar.gz
vmprotect-3.5.1-master.tar.bz2
vmprotect-3.5.1-master.zip
first commitHEADmaster
Diffstat (limited to 'core/osutils.cc')
-rw-r--r--core/osutils.cc2204
1 files changed, 2204 insertions, 0 deletions
diff --git a/core/osutils.cc b/core/osutils.cc
new file mode 100644
index 0000000..2c594a4
--- /dev/null
+++ b/core/osutils.cc
@@ -0,0 +1,2204 @@
+#include "osutils.h"
+#include <fstream>
+
+#ifdef __APPLE__
+#include <CoreServices/CoreServices.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <dlfcn.h>
+#elif defined(__unix__)
+#include <dlfcn.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <langinfo.h>
+#include <pwd.h>
+#else
+#pragma warning( disable : 4091 )
+#include <shlobj.h>
+#pragma warning( default: 4091 )
+#endif
+
+#define FILE_OPEN_MODE(fm) ((fm) & 0xf)
+#define FILE_SHARE_MODE(fm) ((fm) & 0xf0)
+
+namespace os
+{
+
+static const uint8_t utf8_limits[5] = {0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
+
+unicode_string FromUTF8(const std::string &src)
+{
+ unicode_string dest;
+
+ size_t pos = 0;
+ size_t len = src.size();
+ while (pos < len) {
+ uint8_t c = src[pos++];
+
+ if (c < 0x80) {
+ dest += c;
+ continue;
+ }
+
+ size_t val_len;
+ for (val_len = 0; val_len < _countof(utf8_limits); val_len++) {
+ if (c < utf8_limits[val_len])
+ break;
+ }
+
+ if (val_len == 0)
+ continue;
+
+ uint32_t value = c - utf8_limits[val_len - 1];
+ while (val_len) {
+ if (pos == len)
+ break;
+ c = src[pos++];
+ if (c < 0x80 || c >= 0xC0)
+ break;
+ value <<= 6;
+ value |= (c - 0x80);
+ val_len--;
+ }
+
+ if (value < 0x10000) {
+ dest += static_cast<uint16_t>(value);
+ } else if (value <= 0x10FFFF) {
+ value -= 0x10000;
+ dest += static_cast<uint16_t>(0xD800 + (value >> 10));
+ dest += static_cast<uint16_t>(0xDC00 + (value & 0x3FF));
+ }
+ }
+
+ return dest;
+}
+
+#ifdef VMP_GNU
+std::wstring UCS4FromUTF8(const std::string &src)
+{
+ std::wstring dest;
+
+ size_t pos = 0;
+ size_t len = src.size();
+ while (pos < len) {
+ uint8_t c = src[pos++];
+
+ if (c < 0x80) {
+ dest += c;
+ continue;
+ }
+
+ size_t val_len;
+ for (val_len = 0; val_len < _countof(utf8_limits); val_len++) {
+ if (c < utf8_limits[val_len])
+ break;
+ }
+
+ if (val_len == 0)
+ continue;
+
+ uint32_t value = c - utf8_limits[val_len - 1];
+ while (val_len) {
+ if (pos == len)
+ break;
+ c = src[pos++];
+ if (c < 0x80 || c >= 0xC0)
+ break;
+ value <<= 6;
+ value |= (c - 0x80);
+ val_len--;
+ }
+
+ dest += static_cast<wchar_t>(value);
+ }
+
+ return dest;
+}
+#endif
+
+std::string ToUTF8(const unicode_string &src)
+{
+ std::string dest;
+
+ size_t pos = 0;
+ size_t len = src.size();
+ while (pos < len) {
+ uint16_t c = src[pos++];
+ if (c < 0x80) {
+ dest += static_cast<char>(c);
+ continue;
+ }
+
+ uint32_t value;
+ if (c >= 0xD800 && c < 0xE000) {
+ value = 0xFFFD;
+ if (c < 0xDC00 && pos + 1 < len) {
+ uint16_t c2 = src[pos++];
+ if (c2 >= 0xDC00 && c2 < 0xE000)
+ value = 0x10000 + ((c & 0x3FF) << 10) + (c2 & 0x3FF);
+ }
+ } else {
+ value = c;
+ }
+
+ size_t val_len;
+ for (val_len = 1; val_len < 5; val_len++) {
+ if (value < (static_cast<uint32_t>(1) << (val_len * 5 + 6)))
+ break;
+ }
+ dest += static_cast<char>(utf8_limits[val_len - 1] + (value >> (6 * val_len)));
+ while (val_len) {
+ val_len--;
+ dest += static_cast<char>(0x80 + ((value >> (6 * val_len)) & 0x3F));
+ }
+ }
+
+ return dest;
+}
+
+unicode_string StringToFileName(const char *name)
+{
+ unicode_string res = FromUTF8(name);
+ for (size_t i = 0; i < res.size(); i++) {
+ if (res[i] == L'/')
+ res[i] = L'\\';
+ }
+ return res;
+}
+
+bool is_separator(char c)
+{
+ return c == '/'
+#ifndef VMP_GNU
+ || c == '\\'
+#endif
+ ;
+}
+
+static const char *GetFileName(const char *name)
+{
+ const char *res = name;
+ while (*name) {
+ if ((is_separator(name[0])
+#ifndef VMP_GNU
+ || name[0] == ':'
+#endif
+ ) && !is_separator(name[1]))
+ res = name + 1;
+ name++;
+ }
+ return res;
+}
+
+static const char *GetFileExt(const char *name)
+{
+ name = GetFileName(name);
+ const char *res = NULL;
+ while (*name) {
+ if (*name == ' ')
+ res = NULL;
+ else if (*name == '.')
+ res = name;
+ name++;
+ }
+ return res ? res : name;
+}
+
+std::string ExtractFilePath(const char *name)
+{
+ if (!name)
+ return std::string();
+ return std::string(name, static_cast<size_t>(GetFileName(name) - name));
+}
+
+std::string ExtractFileName(const char *name)
+{
+ if (!name)
+ return std::string();
+ return std::string(GetFileName(name));
+}
+
+std::string ExtractFileExt(const char *name)
+{
+ if (!name)
+ return std::string();
+ return std::string(GetFileExt(name));
+}
+
+std::string ChangeFileExt(const char *name, const char *ext)
+{
+ if (!name)
+ return std::string();
+ std::string res = std::string(name, static_cast<size_t>(GetFileExt(name) - name));
+ if (ext)
+ res += ext;
+ return res;
+}
+
+static bool IsRelative(const char *name)
+{
+ if (name && (is_separator(*name)
+#ifndef VMP_GNU
+ || (name[0] && name[1] == ':')
+#endif
+ ))
+ return false;
+ return true;
+}
+
+std::string CombinePaths(const char *path, const char *file_name)
+{
+ if (!path || *path == 0 || !IsRelative(file_name))
+ return std::string(file_name ? file_name : "");
+
+ std::string res = path;
+ if (!res.empty()) {
+#ifdef VMP_GNU
+ if (!is_separator(*(res.end() - 1)))
+ res += '/';
+#else
+ if (!is_separator(res.back()))
+ res += '\\';
+#endif
+ }
+
+ return res + file_name;
+}
+
+std::string SubtractPath(const char *path, const char *file_name)
+{
+ if (!path || *path == 0 || !file_name || IsRelative(file_name))
+ return std::string(file_name ? file_name : "");
+
+ const char *name = file_name;
+ while (*name) {
+ char p = *path++;
+ char f = *name++;
+ if (p != f) {
+ if (is_separator(f) && is_separator(p))
+ continue;
+ if (p == 0)
+ return std::string(is_separator(f) ? name : name - 1);
+ break;
+ }
+ }
+
+ return std::string(file_name);
+}
+
+std::string GetCurrentPath()
+{
+#ifdef VMP_GNU
+ char buff[PATH_MAX];
+ if (!getcwd(buff, sizeof(buff)))
+ buff[0] = 0;
+ return std::string(buff);
+#else
+ wchar_t buff[MAX_PATH];
+ DWORD size = GetCurrentDirectoryW(_countof(buff), buff);
+ return ToUTF8(std::wstring(buff, size));
+#endif
+}
+
+std::string GetExecutablePath()
+{
+ std::string res;
+#ifdef __APPLE__
+ CFURLRef url = CFBundleCopyExecutableURL(CFBundleGetMainBundle());
+ if (url) {
+ CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
+ if (path) {
+ char buffer[PATH_MAX];
+ if (CFStringGetCString(path, buffer, sizeof(buffer), kCFStringEncodingUTF8))
+ res = ExtractFilePath(buffer);
+ CFRelease(path);
+ }
+ CFRelease(url);
+ }
+#elif defined(__unix__)
+ char buff[PATH_MAX];
+ ssize_t len = ::readlink("/proc/self/exe", buff, sizeof(buff)-1);
+ if (len != -1) {
+ buff[len] = '\0';
+ res = ExtractFilePath(buff);
+ }
+#else
+ wchar_t buff[MAX_PATH];
+ DWORD size = GetModuleFileNameW(NULL, buff, _countof(buff));
+ res = ExtractFilePath(ToUTF8(std::wstring(buff, size)).c_str());
+#endif
+ return res;
+}
+
+bool FileExists(const char *name)
+{
+ if (!name)
+ return false;
+#ifdef VMP_GNU
+ struct stat s;
+ int res = stat(name, &s);
+ if (res == -1)
+ return false;
+ return true;
+#else
+ return GetFileAttributesW(FromUTF8(name).c_str()) != INVALID_FILE_ATTRIBUTES;
+#endif
+}
+
+bool FileDelete(const char *name, bool toRecycleBin /*= false*/)
+{
+#ifdef VMP_GNU
+ if(toRecycleBin)
+ {
+#ifdef __APPLE__
+ if (0 == FSPathMoveObjectToTrashSync(name, NULL, kFSFileOperationDefaultOptions))
+ return true;
+#elif defined(__unix__)
+ //using trash from apt-get install trash-cli
+ system((std::string("trash \"") + name + "\"").c_str());
+ if (!FileExists(name))
+ return true;
+#endif
+ }
+ return (remove(name) == 0);
+#else
+ unicode_string uname = FromUTF8(name);
+ if(toRecycleBin)
+ {
+ SHFILEOPSTRUCTW operation = SHFILEOPSTRUCTW();
+ operation.wFunc = FO_DELETE;
+ wchar_t full_path[MAX_PATH + 1];
+ DWORD lengNoTerm = GetFullPathNameW(uname.c_str(), MAX_PATH, full_path, NULL);
+ if(lengNoTerm < MAX_PATH)
+ {
+ full_path[lengNoTerm + 1] = 0; //double terminated
+ operation.pFrom = full_path;
+ operation.fFlags = FOF_ALLOWUNDO | FOF_NO_UI;
+ if(SHFileOperationW(&operation) == 0)
+ return true;
+ }
+ }
+ return (DeleteFileW(uname.c_str()) != FALSE);
+#endif
+}
+
+bool FileCopy(const char *src, const char *dest)
+{
+#ifdef __APPLE__
+ return (copyfile(src, dest, NULL, COPYFILE_ALL) == 0);
+#elif defined (__unix__)
+ try
+ {
+ std::ifstream source(src, std::ios::binary);
+ if (!source.is_open())
+ return false;
+ std::ofstream destination(dest, std::ios::binary);
+ if (!destination.is_open())
+ return false;
+ destination << source.rdbuf();
+ return true;
+ } catch(std::ios_base::failure &)
+ {
+ FileDelete(dest);
+ return false;
+ }
+#else
+ return (CopyFileW(FromUTF8(src).c_str(), FromUTF8(dest).c_str(), false) != FALSE);
+#endif
+}
+
+// fmCreate Create a file with the given name. If a file with the given name exists, open the file in write mode.
+// fmOpenRead Open the file for reading only.
+// fmOpenWrite Open the file for writing only. Writing to the file completely replaces the current contents.
+// fmOpenReadWrite Open the file to modify the current contents rather than replace them.
+
+HANDLE FileCreate(const char *name, uint32_t mode)
+{
+#ifdef VMP_GNU
+ int flags;
+
+ switch (FILE_OPEN_MODE(mode)) {
+ case fmOpenRead:
+ flags = O_RDONLY;
+ break;
+ case fmOpenWrite:
+ flags = O_WRONLY | O_TRUNC;
+ break;
+ case fmOpenReadWrite:
+ flags = O_RDWR;
+ break;
+ default:
+ return INVALID_HANDLE_VALUE;
+ }
+
+ if (mode & fmCreate)
+ flags |= (O_CREAT | O_TRUNC);
+
+ return reinterpret_cast<HANDLE>(open(name, flags, (flags & O_CREAT) ? S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH : 0));
+#else
+ uint32_t desired_access, share_mode;
+
+ switch (FILE_OPEN_MODE(mode)) {
+ case fmOpenRead:
+ desired_access = GENERIC_READ;
+ break;
+ case fmOpenWrite:
+ desired_access = GENERIC_WRITE;
+ break;
+ case fmOpenReadWrite:
+ desired_access = GENERIC_WRITE | GENERIC_READ;
+ break;
+ default:
+ return INVALID_HANDLE_VALUE;
+ }
+
+ switch (FILE_SHARE_MODE(mode)) {
+ case fmShareExclusive:
+ share_mode = 0;
+ break;
+ case fmShareDenyWrite:
+ share_mode = FILE_SHARE_READ;
+ break;
+ case fmShareDenyRead:
+ share_mode = FILE_SHARE_WRITE;
+ break;
+ case fmShareDenyNone:
+ share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ break;
+ default:
+ return INVALID_HANDLE_VALUE;
+ }
+
+ return CreateFileW(FromUTF8(name).c_str(), desired_access, share_mode, NULL, (mode & fmCreate) ? CREATE_ALWAYS : OPEN_EXISTING, 0, NULL);
+#endif
+}
+
+bool FileClose(HANDLE h)
+{
+#ifdef VMP_GNU
+ return (close(h) == 0);
+#else
+ return (CloseHandle(h) != 0);
+#endif
+}
+
+size_t FileRead(HANDLE h, void *buf, size_t size)
+{
+#ifdef VMP_GNU
+ size_t res;
+ res = read(h, buf, size);
+#else
+ size_t res = 0;
+ DWORD portion;
+ while(size > 0) {
+ portion = (DWORD)-1;
+ if (portion > size)
+ portion = (DWORD)size;
+ if (ReadFile(h, (char *)buf + res, portion, &portion, NULL) == 0)
+ return (size_t)-1;
+
+ res += portion;
+ size -= portion;
+ if (portion == 0)
+ break;
+ }
+#endif
+ return res;
+}
+
+size_t FileWrite(HANDLE h, const void *buf, size_t size)
+{
+#ifdef VMP_GNU
+ size_t res;
+ res = write(h, buf, size);
+#else
+ size_t res = 0;
+ DWORD portion;
+ while(size > 0) {
+ portion = (DWORD)-1;
+ if (portion > size)
+ portion = (DWORD)size;
+ if (WriteFile(h, (const char *)buf + res, portion, &portion, NULL) == 0)
+ return (size_t)-1;
+
+ res += portion;
+ size -= portion;
+ if (portion == 0)
+ break;
+ }
+#endif
+ return res;
+}
+
+uint64_t FileSeek(HANDLE h, uint64_t offset, SeekOrigin origin)
+{
+ uint64_t res;
+#ifdef VMP_GNU
+ int method;
+
+ switch (origin) {
+ case soBeginning:
+ method = SEEK_SET;
+ break;
+ case soCurrent:
+ method = SEEK_CUR;
+ break;
+ case soEnd:
+ method = SEEK_END;
+ break;
+ default:
+ return -1;
+ }
+
+ res = lseek(h, offset, method);
+#else
+ uint32_t method;
+
+ switch (origin) {
+ case soBeginning:
+ method = FILE_BEGIN;
+ break;
+ case soCurrent:
+ method = FILE_CURRENT;
+ break;
+ case soEnd:
+ method = FILE_END;
+ break;
+ default:
+ return -1;
+ }
+
+ uint32_t pos_low = static_cast<uint32_t>(offset);
+ uint32_t pos_high = static_cast<uint32_t>(offset >> 32);
+ pos_low = SetFilePointer(h, pos_low, (PLONG)&pos_high, method);
+ if (pos_low == INVALID_SET_FILE_POINTER && GetLastError() != 0)
+ return -1;
+ res = (static_cast<uint64_t>(pos_high) << 32) | pos_low;
+#endif
+ return res;
+}
+
+bool FileSetEnd(HANDLE h)
+{
+#ifdef VMP_GNU
+ uint64_t pos = lseek(h, 0, SEEK_CUR);
+ if (pos == (uint64_t)-1)
+ return false;
+ return (ftruncate(h, pos) == 0);
+#else
+ return (SetEndOfFile(h) != 0);
+#endif
+}
+
+bool FileGetCheckSum(const char *file_name, uint32_t *check_sum)
+{
+ if (!file_name)
+ return false;
+
+ bool res = false;
+ HANDLE file_handle = FileCreate(file_name, fmOpenRead | fmShareDenyNone);
+ if (file_handle != INVALID_HANDLE_VALUE) {
+#ifdef VMP_GNU
+ size_t file_size = lseek(file_handle, 0, SEEK_END);
+#else
+ uint32_t file_size = GetFileSize(file_handle, NULL);
+ HANDLE file_map = CreateFileMappingW(file_handle, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (file_map) {
+#endif
+
+#ifdef VMP_GNU
+ uint16_t *file_view = reinterpret_cast<uint16_t *>(mmap(0, file_size, PROT_READ, MAP_SHARED, file_handle, 0));
+ if (file_view != MAP_FAILED) {
+#else
+ uint16_t *file_view = static_cast<uint16_t *>(MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 0));
+ if (file_view) {
+#endif
+ bool is_valid_format = false;
+ uint32_t header_sum = 0;
+ IMAGE_DOS_HEADER *dos_header = reinterpret_cast<IMAGE_DOS_HEADER *>(file_view);
+ if (dos_header->e_magic == IMAGE_DOS_SIGNATURE) {
+ IMAGE_NT_HEADERS32 *header_32 = reinterpret_cast<IMAGE_NT_HEADERS32 *>(reinterpret_cast<uint8_t *>(file_view) + dos_header->e_lfanew);
+ if (header_32->Signature == IMAGE_NT_SIGNATURE) {
+ if (header_32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ header_sum = header_32->OptionalHeader.CheckSum;
+ is_valid_format = true;
+ } else if (header_32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ header_sum = reinterpret_cast<IMAGE_NT_HEADERS64 *>(header_32)->OptionalHeader.CheckSum;
+ is_valid_format = true;
+ }
+ }
+ }
+
+ if (is_valid_format) {
+ uint32_t sum = 0;
+ size_t c = (file_size + 1) / sizeof(uint16_t);
+ for (size_t i = 0; i < c; i++) {
+ sum += file_view[i];
+ if (HIWORD(sum))
+ sum = LOWORD(sum) + HIWORD(sum);
+ }
+ sum = static_cast<uint16_t>(LOWORD(sum) + HIWORD(sum));
+
+ if (LOWORD(sum) >= LOWORD(header_sum)) {
+ sum -= LOWORD(header_sum);
+ } else {
+ sum = ((LOWORD(sum) - LOWORD(header_sum)) & 0xFFFF) - 1;
+ }
+
+ if (LOWORD(sum) >= HIWORD(header_sum)) {
+ sum -= HIWORD(header_sum);
+ } else {
+ sum = ((LOWORD(sum) - HIWORD(header_sum)) & 0xFFFF) - 1;
+ }
+ sum += file_size;
+
+ *check_sum = sum;
+ res = true;
+ }
+
+#ifdef VMP_GNU
+ munmap(file_view, file_size);
+#else
+ UnmapViewOfFile(file_view);
+#endif
+#ifndef VMP_GNU
+ }
+ CloseHandle(file_map);
+#endif
+ }
+#ifdef VMP_GNU
+ close(file_handle);
+#else
+ CloseHandle(file_handle);
+#endif
+ }
+
+ return res;
+}
+
+#ifndef VMP_GNU
+std::string ToOEM(const unicode_string &src)
+{
+ std::string res;
+ if (!src.empty()) {
+ int size = WideCharToMultiByte(CP_OEMCP, 0, src.c_str(), (int)src.size(), NULL, 0, NULL, NULL);
+ if (size > 0) {
+ res.resize(size);
+ WideCharToMultiByte(CP_OEMCP, 0, src.c_str(), (int)src.size(), &res[0], (int)res.size(), NULL, NULL);
+ }
+ }
+ return res;
+}
+unicode_string FromACP(const std::string &src)
+{
+ unicode_string res;
+ if (!src.empty()) {
+ int size = MultiByteToWideChar(CP_ACP, 0, src.c_str(), (int)src.size(), NULL, 0);
+ if (size > 0) {
+ res.resize(size);
+ MultiByteToWideChar(CP_ACP, 0, src.c_str(), (int)src.size(), &res[0], (int)res.size());
+ }
+ }
+ return res;
+}
+bool ValidateUTF8(const std::string &src)
+{
+ bool ret = true;
+ if (!src.empty()) {
+ int res = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src.c_str(), -1, NULL, NULL);
+ if (res == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
+ ret = false;
+ }
+ }
+ return ret;
+}
+#endif
+
+ void Print(const char *text)
+ {
+ #ifdef VMP_GNU
+ std::cout << text << std::flush;
+ #else
+ std::cout << ToOEM(FromUTF8(text)) << std::flush;
+ #endif
+ }
+
+std::vector<std::string> CommandLine()
+{
+ std::vector<std::string> res;
+#ifdef __APPLE__
+ int num = *_NSGetArgc();
+ char **args = *_NSGetArgv();
+ for (int i = 0; i < num; i++) {
+ if (args[i])
+ res.push_back(std::string(args[i]));
+ }
+#elif defined(__unix__)
+ FILE *cmdline = fopen("/proc/self/cmdline", "rb");
+ char *arg = 0;
+ size_t size = 0;
+ if (cmdline)
+ {
+ while (getdelim(&arg, &size, 0, cmdline) != -1)
+ {
+ res.push_back(std::string(arg));
+ }
+ }
+ free(arg);
+ fclose(cmdline);
+#else
+ int num;
+ wchar_t **args = CommandLineToArgvW(GetCommandLineW(), &num);
+ for (int i = 0; i < num; i++) {
+ if (args[i])
+ res.push_back(ToUTF8(std::wstring(args[i])));
+ }
+#endif
+ return res;
+}
+
+#ifdef VMP_GNU
+#define TOUPPER(x) toupper(x)
+static bool WildcardMatch(const char *name, const char *mask)
+#else
+#define TOUPPER(x) towupper(x)
+static bool WildcardMatch(const wchar_t *name, const wchar_t *mask)
+#endif
+{
+ if (!name || !mask)
+ return false;
+
+ while (*name) {
+ if (*mask == '*') {
+ mask++;
+ if (*mask == '\0')
+ return true;
+ while (*name) {
+ if (WildcardMatch(name, mask))
+ return true;
+ name++;
+ }
+ return false;
+ }
+ if (*mask == '?' || TOUPPER(*name) == TOUPPER(*mask)) {
+ name++;
+ mask++;
+ } else {
+ return false;
+ }
+ }
+ while (*mask == '*')
+ mask++;
+ return (*mask == '\0');
+}
+
+std::vector<std::string> FindFiles(const char *name, const char *mask, bool only_directories)
+{
+ std::vector<std::string> res;
+ if (!name || !mask)
+ return res;
+
+#ifdef VMP_GNU
+ DIR *dir;
+ struct dirent *ent;
+ dir = opendir(name);
+ if (dir) {
+ while ((ent = readdir(dir))) {
+ if (WildcardMatch(ent->d_name, mask))
+ res.push_back(CombinePaths(name, ent->d_name));
+ }
+ closedir(dir);
+ }
+#else
+ std::wstring unicode_mask = FromUTF8(mask);
+ WIN32_FIND_DATAW find_data;
+ HANDLE h = FindFirstFileW(FromUTF8(std::string(name) + "\\*").c_str(), &find_data);
+ if (h != INVALID_HANDLE_VALUE) {
+ do {
+ if (wcscmp(find_data.cFileName, L".") == 0 || wcscmp(find_data.cFileName, L"..") == 0)
+ continue;
+ if (only_directories && ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0))
+ continue;
+ if (!WildcardMatch(find_data.cFileName, unicode_mask.c_str()))
+ continue;
+
+ res.push_back(CombinePaths(name, ToUTF8(find_data.cFileName).c_str()));
+ } while (FindNextFileW(h, &find_data) != 0);
+ FindClose(h);
+ }
+#endif
+ return res;
+}
+
+uint32_t GetTickCount()
+{
+#ifdef __APPLE__
+ const int64_t one_million = 1000 * 1000;
+ mach_timebase_info_data_t timebase_info;
+ mach_timebase_info(&timebase_info);
+
+ // mach_absolute_time() returns billionth of seconds,
+ // so divide by one million to get milliseconds
+ return static_cast<uint32_t>((mach_absolute_time() * timebase_info.numer) / (one_million * timebase_info.denom));
+#elif defined (__unix__)
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+#else
+ return ::GetTickCount();
+#endif
+}
+
+#ifdef VMP_GNU
+static char* rstrip(char* s)
+{
+ char* p = s + strlen(s);
+ while (p > s && isspace((unsigned char)(*--p)))
+ *p = '\0';
+ return s;
+}
+
+static char* lskip(const char* s)
+{
+ while (*s && isspace((unsigned char)(*s)))
+ s++;
+ return (char*)s;
+}
+
+static char* find_char_or_comment(const char* s, char c)
+{
+ int was_whitespace = 0;
+ while (*s && *s != c && !(was_whitespace && *s == ';')) {
+ was_whitespace = isspace((unsigned char)(*s));
+ s++;
+ }
+ return (char*)s;
+}
+
+static void SwapBuffer(unicode_char *buf, size_t size)
+{
+ for (size_t i = 0; i < size; i++) {
+ unicode_char c = buf[i];
+ buf[i] = (c >> 8) | (c << 8);
+ }
+}
+
+bool ProfileString(const char *_section, const char *_key, const char *_value, char const *file_name, std::string *result)
+{
+ static const uint8_t utf16_le_bom[] = {0xFF, 0xFE};
+ static const uint8_t utf16_be_bom[] = {0xFE, 0xFF};
+ static const uint8_t utf8_bom[] = {0xEF, 0xBB, 0xBF};
+
+ enum Encoding {
+ enNone,
+ enUTF16_le,
+ enUTF16_be,
+ enUTF8
+ };
+
+ char *start;
+ char *end;
+ char *section;
+ char *name;
+ char *value;
+ Encoding encoding = enNone;
+ std::stringstream stream;
+
+ HANDLE file = FileCreate(file_name, fmOpenRead | fmShareDenyNone);
+ if (file == INVALID_HANDLE_VALUE) {
+ if (result)
+ return false;
+ } else {
+ char bom[3] = {0};
+ FileRead(file, bom, sizeof(utf16_le_bom));
+ if (memcmp(bom, utf16_le_bom, sizeof(utf16_le_bom)) == 0) {
+ encoding = enUTF16_le;
+ } else if (memcmp(bom, utf16_be_bom, sizeof(utf16_be_bom)) == 0) {
+ encoding = enUTF16_be;
+ } else {
+ FileRead(file, &bom[2], 1);
+ if (memcmp(bom, utf8_bom, sizeof(utf8_bom)) == 0)
+ encoding = enUTF8;
+ }
+ uint64_t begin_pos = (encoding == enNone) ? 0 : FileSeek(file, 0, soCurrent);
+ uint64_t end_pos = FileSeek(file, 0, soEnd);
+ size_t size = static_cast<size_t>(end_pos - begin_pos);
+ if (size) {
+ FileSeek(file, begin_pos, soBeginning);
+ uint8_t *buff = new uint8_t[size];
+ FileRead(file, buff, size);
+ FileClose(file);
+ if (encoding == enUTF16_be)
+ SwapBuffer(reinterpret_cast<unicode_char *>(buff), size / sizeof(unicode_char));
+ stream.str((encoding == enUTF16_le || encoding == enUTF16_be) ? ToUTF8(unicode_string(reinterpret_cast<unicode_char *>(buff), size / sizeof(unicode_char))) : std::string(reinterpret_cast<char *>(buff), size));
+ delete [] buff;
+ }
+ FileClose(file);
+ }
+
+ bool section_found = false;
+ bool value_saved = false;
+ std::string line;
+ std::vector<std::string> lines;
+ while (std::getline(stream, line)) {
+ if (!line.empty() && *(line.end() - 1) == '\r')
+ line.erase(line.size() - 1);
+
+ if (!result)
+ lines.push_back(line);
+
+ start = &line[0];
+ start = lskip(rstrip(start));
+
+ if (*start == ';' || *start == '#') {
+ /* Per Python ConfigParser, allow '#' comments at start of line */
+ } else if (*start == '[') {
+ /* A "[section]" line */
+ end = find_char_or_comment(start + 1, ']');
+ if (*end == ']') {
+ *end = '\0';
+ if (section_found) {
+ if (_key) {
+ if (result) {
+ return false;
+ } else if (!value_saved) {
+ std::vector<std::string>::iterator it = lines.end() - 1;
+ if (_value)
+ lines.insert(it, std::string(_key) + std::string("=") + std::string(_value));
+ else
+ lines.erase(it);
+ value_saved = true;
+ }
+ } else {
+ if (result)
+ return true;
+ }
+ }
+
+ section = start + 1;
+ section_found = (_strcmpi(section, _section) == 0);
+ }
+ } else if (*start && *start != ';') {
+ /* Not a comment, must be a name[=:]value pair */
+ end = find_char_or_comment(start, '=');
+ if (*end != '=') {
+ end = find_char_or_comment(start, ':');
+ }
+ if (*end == '=' || *end == ':') {
+ *end = '\0';
+ name = rstrip(start);
+ value = lskip(end + 1);
+ end = find_char_or_comment(value, '\0');
+ if (*end == ';')
+ *end = '\0';
+ rstrip(value);
+
+ if (section_found) {
+ if (_key) {
+ if (_strcmpi(name, _key) == 0) {
+ if (result) {
+ *result = value;
+ return true;
+ } else if (!value_saved) {
+ std::vector<std::string>::iterator it = lines.end() - 1;
+ if (_value)
+ *it = std::string(name) + "=" + std::string(_value);
+ else
+ lines.erase(it);
+ value_saved = true;
+ }
+ }
+ } else {
+ if (result) {
+ *result += name;
+ *result += '\0';
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (result) {
+ return false;
+ } else {
+ file = FileCreate(file_name, fmCreate | fmOpenWrite | fmShareDenyNone);
+ if (file == INVALID_HANDLE_VALUE)
+ return false;
+
+ switch (encoding) {
+ case enUTF8:
+ FileWrite(file, utf8_bom, sizeof(utf8_bom));
+ break;
+ case enUTF16_le:
+ FileWrite(file, utf16_le_bom, sizeof(utf16_le_bom));
+ break;
+ case enUTF16_be:
+ FileWrite(file, utf16_be_bom, sizeof(utf16_be_bom));
+ break;
+ }
+
+ if (!value_saved) {
+ if (!section_found)
+ lines.push_back(std::string("[") + std::string(_section) + std::string("]"));
+ lines.push_back(std::string(_key) + std::string("=") + std::string(_value));
+ }
+
+ for (size_t i = 0; i < lines.size(); i++) {
+ std::string str = lines[i] + "\r\n";
+ if (encoding == enUTF16_le || encoding == enUTF16_be) {
+ unicode_string wstr = FromUTF8(str);
+ if (encoding == enUTF16_be)
+ SwapBuffer(const_cast<unicode_char *>(wstr.c_str()), wstr.size());
+ FileWrite(file, wstr.c_str(), wstr.size() * sizeof(unicode_char));
+ } else {
+ FileWrite(file, str.c_str(), str.size());
+ }
+ }
+ FileClose(file);
+ return true;
+ }
+}
+#endif
+
+bool WriteIniString(const char *section, const char *key, const char *value, const char *file_name)
+{
+#ifdef VMP_GNU
+ return ProfileString(section, key, value, file_name, NULL);
+#else
+ return WritePrivateProfileStringW(section ? FromUTF8(section).c_str() : NULL,
+ key ? os::FromUTF8(key).c_str() : NULL,
+ value ? os::FromUTF8(value).c_str() : NULL,
+ file_name ? os::FromUTF8(file_name).c_str() : NULL
+ ) != FALSE;
+#endif
+}
+
+std::string ReadIniString(const char *section, const char *key, const char *default_value, const char *file_name)
+{
+ std::string res;
+#ifdef VMP_GNU
+ if (!ProfileString(section, key, default_value, file_name, &res)) {
+ if (default_value)
+ res = std::string(default_value);
+ }
+#else
+ size_t buffer_size = 1024;
+ os::unicode_char *buffer = NULL;
+ for (;;) {
+ delete [] buffer;
+ buffer = new os::unicode_char[buffer_size];
+ uint32_t len = GetPrivateProfileStringW(section ? FromUTF8(section).c_str() : NULL,
+ key ? FromUTF8(key).c_str() : NULL,
+ default_value ? FromUTF8(default_value).c_str() : NULL,
+ buffer, (DWORD)buffer_size,
+ file_name ? FromUTF8(file_name).c_str() : NULL);
+ if (len < buffer_size - sizeof(os::unicode_char)) {
+ res = os::ToUTF8(os::unicode_string(buffer, len));
+ break;
+ }
+ buffer_size *= 4;
+ }
+ delete [] buffer;
+#endif
+ return res;
+}
+
+HPROCESS ProcessOpen(uint32_t id)
+{
+#ifdef __APPLE__
+ mach_port_t task;
+ if (task_for_pid(mach_task_self(), id, &task) != KERN_SUCCESS)
+ return 0;
+ return task;
+#elif defined(__unix__)
+ return HPROCESS(id);
+#else
+ return OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);
+#endif
+}
+
+bool ProcessClose(HPROCESS h)
+{
+#ifdef VMP_GNU
+ // do nothing
+ return true;
+#else
+ return (CloseHandle(h) != 0);
+#endif
+}
+
+size_t ProcessRead(HPROCESS h, void *base_address, void *buf, size_t size)
+{
+#ifdef __APPLE__
+ mach_vm_size_t res;
+ if (mach_vm_read_overwrite(h, (mach_vm_address_t)base_address, size, (mach_vm_address_t)buf, &res) != KERN_SUCCESS)
+ return -1;
+ return res;
+#elif defined(__unix__)
+ struct iovec local, remote;
+ local.iov_base = buf;
+ local.iov_len = (int)size;
+ remote.iov_base = base_address;
+ local.iov_len = (int)size;
+ ssize_t nread = process_vm_readv(reinterpret_cast<pid_t>(h), &local, 1, &remote, 1, 0);
+ return (size_t)nread;
+#else
+ SIZE_T res;
+ if (ReadProcessMemory(h, base_address, buf, size, &res) == 0)
+ return -1;
+ return res;
+#endif
+}
+
+size_t ProcessWrite(HPROCESS h, void *base_address, const void *buf, size_t size)
+{
+#ifdef __APPLE__
+ if (mach_vm_write(h, (mach_vm_address_t)base_address, size, (mach_vm_address_t)buf) != KERN_SUCCESS)
+ return -1;
+ return size;
+#elif defined(__unix__)
+ struct iovec local, remote;
+ local.iov_base = const_cast<void *>(buf);
+ local.iov_len = (int)size;
+ remote.iov_base = base_address;
+ local.iov_len = (int)size;
+ ssize_t nwrite = process_vm_writev(reinterpret_cast<pid_t>(h), &local, 1, &remote, 1, 0);
+ return (size_t)nwrite;
+#else
+ SIZE_T res;
+ if (WriteProcessMemory(h, base_address, buf, size, &res) == 0)
+ return -1;
+ return res;
+#endif
+}
+
+uint64_t GetLastWriteTime(const char *name)
+{
+ uint64_t res = 0;
+ HANDLE h = FileCreate(name, fmOpenRead | fmShareDenyNone);
+ if (h != INVALID_HANDLE_VALUE) {
+#ifdef VMP_GNU
+ struct stat stat_buf;
+ if (fstat(h, &stat_buf) == 0)
+ res = stat_buf.st_mtime;
+#else
+ FILETIME file_time;
+ if (GetFileTime(h, NULL, NULL , &file_time))
+ res = (static_cast<uint64_t>(file_time.dwHighDateTime) << 32 | file_time.dwLowDateTime) / 10000000 - 11644473600;
+#endif
+ FileClose(h);
+ }
+ return res;
+}
+
+std::vector<PROCESS_ITEM> EnumProcesses()
+{
+ std::vector<PROCESS_ITEM> res;
+#ifdef __APPLE__
+ int err;
+ static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
+ size_t length;
+
+ bool done = false;
+ kinfo_proc *processes = NULL;
+ do {
+ length = 0;
+ err = sysctl(const_cast<int *>(name), (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0);
+ if (err == -1)
+ err = errno;
+
+ if (err == 0) {
+ processes = new kinfo_proc[length];
+ if (processes == NULL)
+ err = ENOMEM;
+ }
+
+ if (err == 0) {
+ err = sysctl(const_cast<int *>(name), (sizeof(name) / sizeof(*name)) - 1, processes, &length, NULL, 0);
+ if (err == -1)
+ err = errno;
+ if (err == 0) {
+ done = true;
+ } else if (err == ENOMEM) {
+ delete [] processes;
+ processes = NULL;
+ err = 0;
+ }
+ }
+ } while (err == 0 && !done);
+
+ mach_port_t task;
+ if (err == 0 && processes) {
+ for (size_t i = 0; i < length / sizeof(kinfo_proc); i++) {
+ kinfo_proc *process = &processes[i];
+
+ if (task_for_pid(mach_task_self(), process->kp_proc.p_pid, &task) != KERN_SUCCESS)
+ continue;
+
+ PROCESS_ITEM item;
+ item.id = process->kp_proc.p_pid;
+ item.name = process->kp_proc.p_comm;
+ res.push_back(item);
+ }
+ }
+ delete [] processes;
+#elif defined(__unix__)
+ struct dirent* dent;
+ DIR* srcdir = opendir("/proc");
+ if (srcdir != NULL)
+ {
+ while((dent = readdir(srcdir)) != NULL)
+ {
+ struct stat st;
+
+ if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
+ continue;
+
+ if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) >= 0 && S_ISDIR(st.st_mode))
+ {
+ PROCESS_ITEM item;
+ item.id = atoi(dent->d_name);
+ if (item.id == 0)
+ continue;
+ char path[4096];
+ snprintf(path, sizeof(path), "/proc/%d/maps", item.id);
+ FILE *fmaps = fopen(path, "r");
+ if (fmaps)
+ {
+ char c;
+ size_t read = fread(&c, 1, 1, fmaps);
+ fclose(fmaps);
+ if (read == 1)
+ {
+ snprintf(path, sizeof(path), "/proc/%d/comm", item.id);
+ std::ifstream f(path);
+ std::stringstream buffer;
+ buffer << f.rdbuf();
+ item.name = buffer.str();
+ size_t endpos = item.name.find_last_not_of("\r\n");
+ if( std::string::npos != endpos )
+ item.name = item.name.substr( 0, endpos+1 );
+ res.push_back(item);
+ }
+ }
+ }
+ }
+ }
+ closedir(srcdir);
+#else
+ DWORD processes[1024], needed;
+ if (::EnumProcesses(processes, sizeof(processes), &needed)) {
+ size_t count = needed / sizeof(DWORD);
+ for (size_t i = 0; i < count; i++) {
+ if (!processes[i])
+ continue;
+
+ wchar_t process_name[MAX_PATH] = {0};
+ HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processes[i]);
+ if (process) {
+ if (GetModuleFileNameExW(process, 0, process_name, _countof(process_name))) {
+ PROCESS_ITEM item;
+ item.id = processes[i];
+ item.name = ExtractFileName(ToUTF8(process_name).c_str());
+ res.push_back(item);
+ }
+ CloseHandle(process);
+ }
+ }
+ }
+#endif
+ return res;
+}
+
+#ifdef __unix__
+/*
+address perms offset dev inode pathname
+00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/dbus-daemon
+00651000-00652000 r--p 00051000 08:02 173521 /usr/bin/dbus-daemon
+00652000-00655000 rw-p 00052000 08:02 173521 /usr/bin/dbus-daemon
+00e03000-00e24000 rw-p 00000000 00:00 0 [heap]
+00e24000-011f7000 rw-p 00000000 00:00 0 [heap]
+...
+35b1800000-35b1820000 r-xp 00000000 08:02 135522 /usr/lib64/ld-2.15.so
+35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522 /usr/lib64/ld-2.15.so
+35b1a20000-35b1a21000 rw-p 00020000 08:02 135522 /usr/lib64/ld-2.15.so
+35b1a21000-35b1a22000 rw-p 00000000 00:00 0
+35b1c00000-35b1dac000 r-xp 00000000 08:02 135870 /usr/lib64/libc-2.15.so
+35b1dac000-35b1fac000 ---p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so
+35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so
+35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870 /usr/lib64/libc-2.15.so
+...
+f2c6ff8c000-7f2c7078c000 rw-p 00000000 00:00 0 [stack:986]
+...
+7fffb2c0d000-7fffb2c2e000 rw-p 00000000 00:00 0 [stack]
+7fffb2d48000-7fffb2d49000 r-xp 00000000 00:00 0 [vdso]
+*/
+static bool ParseMapsLine(const char *maps, int *inode, char *name, size_t cch_name, uint64_t *from, uint64_t *to, uint64_t *offset)
+{
+ bool res = false;
+ *inode = 0;
+ *name = 0;
+ *from = *to = *offset = 0;
+ if ( strlen(maps) < cch_name &&
+ sscanf_s(maps, "%llx-%llx%*[ \trwxp-]%llx%*[ \t]%*d:%*d%*[ \t]%d%*[ \t]%s", from, to, offset, inode, name) == 5 &&
+ *inode != 0)
+ {
+ res = true;
+ }
+ return res;
+}
+#endif
+
+std::vector<MODULE_ITEM> EnumModules(uint32_t process_id)
+{
+ std::vector<MODULE_ITEM> res;
+#ifdef __APPLE__
+ mach_port_t task;
+ if (task_for_pid(mach_task_self(), process_id, &task) == KERN_SUCCESS) {
+ struct task_dyld_info dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ if (task_info(task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count) == KERN_SUCCESS) {
+ if (dyld_info.all_image_info_addr != 0 && dyld_info.all_image_info_size != 0) {
+ dyld_all_image_infos image_infos;
+ if (ProcessRead(task, (void *)dyld_info.all_image_info_addr, &image_infos, sizeof(image_infos)) != (size_t)-1 && image_infos.infoArrayCount) {
+ dyld_image_info *info_array = new dyld_image_info[image_infos.infoArrayCount];
+ if (ProcessRead(task, (void *)image_infos.infoArray, info_array, sizeof(dyld_image_info) * image_infos.infoArrayCount) != (size_t)-1) {
+ for (size_t i = 0; i < image_infos.infoArrayCount; i++) {
+ dyld_image_info *info = &info_array[i];
+
+ std::string name;
+ char c;
+ while (ProcessRead(task, (void *)(info->imageFilePath + name.size()), &c, sizeof(c)) != (size_t)-1) {
+ if (!c)
+ break;
+ name += c;
+ }
+
+ MODULE_ITEM item;
+ item.handle = (HMODULE)info->imageLoadAddress;
+ item.name = name;
+ res.push_back(item);
+ }
+ }
+ delete [] info_array;
+ }
+ }
+ }
+ }
+#elif defined(__unix__)
+ char maps[2048], name[2048];
+ snprintf(maps, sizeof(maps), "/proc/%d/maps", process_id);
+ FILE *fmaps = fopen(maps, "r");
+ if (fmaps)
+ {
+ while (fgets(maps, sizeof(maps), fmaps))
+ {
+ MODULE_ITEM item;
+ int inode;
+ uint64_t from, to, offset;
+ if(ParseMapsLine(maps, &inode, name, sizeof(name), &from, &to, &offset))
+ {
+ item.handle = reinterpret_cast<HMODULE>(from);
+ item.name = name;
+ res.push_back(item);
+ }
+ }
+ fclose(fmaps);
+ }
+#else
+ HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_id);
+ if (process) {
+ HMODULE mods[1024];
+ DWORD needed;
+
+ if (EnumProcessModules(process, mods, sizeof(mods), &needed)) { //-V
+ size_t count = needed / sizeof(HMODULE);
+ for (size_t i = 0; i < count; i++) {
+ wchar_t module_name[MAX_PATH] = {0};
+ if (GetModuleFileNameExW(process, mods[i], module_name, _countof(module_name))) {
+ MODULE_ITEM item;
+ item.handle = mods[i];
+ item.name = ToUTF8(module_name);
+ res.push_back(item);
+ }
+ }
+ }
+ CloseHandle(process);
+ }
+#endif
+ return res;
+}
+
+bool GetModuleInformation(HANDLE process, HMODULE module, MODULE_INFO *info, size_t size)
+{
+ if (size < sizeof(MODULE_INFO))
+ return false;
+
+#ifdef __APPLE__
+ uint8_t *address = static_cast<uint8_t *>(module);
+ mach_header header;
+ if (ProcessRead(process, address, &header, sizeof(header)) == (size_t)-1)
+ return false;
+
+ if (header.magic == MH_MAGIC) {
+ info->address = address;
+ address += sizeof(mach_header);
+ uint32_t min_address = 0;
+ uint32_t max_address = 0;
+ for (size_t i = 0; i < header.ncmds; i++) {
+ load_command command;
+ if (ProcessRead(process, address, &command, sizeof(command)) == (size_t)-1)
+ return false;
+
+ if (command.cmd == LC_SEGMENT) {
+ segment_command segment;
+ if (ProcessRead(process, address, &segment, sizeof(segment)) == (size_t)-1)
+ return false;
+
+ if (segment.vmaddr) {
+ if (!min_address)
+ min_address = segment.vmaddr;
+ if (max_address < segment.vmaddr + segment.vmsize)
+ max_address = segment.vmaddr + segment.vmsize;
+ }
+ }
+ address += command.cmdsize;
+ }
+ info->size = max_address - min_address;
+ } else if (header.magic == MH_MAGIC_64) {
+ info->address = address;
+ address += sizeof(mach_header_64);
+ uint64_t min_address = 0;
+ uint64_t max_address = 0;
+ for (size_t i = 0; i < header.ncmds; i++) {
+ load_command command;
+ if (ProcessRead(process, address, &command, sizeof(command)) == (size_t)-1)
+ return false;
+
+ if (command.cmd == LC_SEGMENT_64) {
+ segment_command_64 segment;
+ if (ProcessRead(process, address, &segment, sizeof(segment)) == (size_t)-1)
+ return false;
+ if (segment.vmaddr) {
+ if (!min_address)
+ min_address = segment.vmaddr;
+ if (max_address < segment.vmaddr + segment.vmsize)
+ max_address = segment.vmaddr + segment.vmsize;
+ }
+ }
+ address += command.cmdsize;
+ }
+ info->size = static_cast<size_t>(max_address - min_address);
+ } else {
+ return false;
+ }
+
+ return true;
+#elif defined(__unix__)
+ bool ret = false;
+ char maps[1024], name[2048];
+ snprintf(maps, sizeof(maps), "/proc/%d/maps", (int)process);
+ FILE *fmaps = fopen(maps, "r");
+ if (fmaps)
+ {
+ while (fgets(maps, sizeof(maps), fmaps))
+ {
+ int inode;
+ uint64_t from, to, offset;
+ if(ParseMapsLine(maps, &inode, name, sizeof(name), &from, &to, &offset) &&
+ reinterpret_cast<HMODULE>(from) == module)
+ {
+ info->address = (void *)from;
+ info->size = to - from;
+ ret = true;
+ }
+ }
+ fclose(fmaps);
+ }
+ return ret;
+#else
+ MODULEINFO moduleInfo;
+ if (!::GetModuleInformation(process, module, &moduleInfo, sizeof(moduleInfo)))
+ return false;
+
+ info->address = moduleInfo.lpBaseOfDll;
+ info->size = moduleInfo.SizeOfImage;
+ return true;
+#endif
+}
+
+std::string GetSysAppDataDirectory()
+{
+ std::string res;
+#ifdef __APPLE__
+ FSRef ref;
+ if (FSFindFolder(kOnAppropriateDisk, kSharedUserDataFolderType, kDontCreateFolder, &ref) == 0) {
+ CFURLRef url_ref = CFURLCreateFromFSRef(NULL, &ref);
+ if (url_ref) {
+ char buffer[PATH_MAX];
+ if (CFURLGetFileSystemRepresentation(url_ref, true, (uint8_t*)buffer, sizeof(buffer)))
+ res = std::string(buffer);
+ CFRelease(url_ref);
+ }
+ }
+#elif defined(__unix__)
+ const char *homedir;
+
+ if ((homedir = getenv("HOME")) == NULL) {
+ homedir = getpwuid(getuid())->pw_dir;
+ }
+ res = std::string(homedir) + "/.config"; //admin should use hard links to map this stuff to /usr/share etc
+#else
+ os::unicode_char buffer[MAX_PATH];
+
+ if (SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, buffer) >= 0)
+ res = os::ToUTF8(buffer);
+#endif
+ return res;
+}
+
+bool DirectoryCreate(const char *name)
+{
+ if (!name)
+ return false;
+
+#ifdef VMP_GNU
+ return (mkdir(name, S_IRWXU | S_IRWXG | S_IRWXO) == 0);
+#else
+ return (CreateDirectoryW(FromUTF8(name).c_str(), NULL) != 0 || GetLastError() == ERROR_ALREADY_EXISTS);
+#endif
+}
+
+bool PathCreate(const char *name)
+{
+ if (!name)
+ return false;
+
+ bool res = DirectoryCreate(name);
+ if (!res) {
+ const char *prev = GetFileName(name);
+ if (prev != name && PathCreate(std::string(name, prev - name - 1).c_str()))
+ res = DirectoryCreate(name);
+ }
+ return res;
+}
+
+#ifndef VMP_GNU
+struct LocaleInfo {
+ LCID id;
+ char iso_name[6];
+};
+
+static const LocaleInfo win_locale_info[] = {
+ {0x0436, "af"},
+ {0x3801, "ar_AE"},
+ {0x3c01, "ar_BH"},
+ {0x1401, "ar_DZ"},
+ {0x0c01, "ar_EG"},
+ {0x0801, "ar_IQ"},
+ {0x2c01, "ar_JO"},
+ {0x3401, "ar_KW"},
+ {0x3001, "ar_LB"},
+ {0x1001, "ar_LY"},
+ {0x1801, "ar_MA"},
+ {0x2001, "ar_OM"},
+ {0x4001, "ar_QA"},
+ {0x0401, "ar_SA"},
+ {0x2801, "ar_SY"},
+ {0x1c01, "ar_TN"},
+ {0x2401, "ar_YE"},
+ {0x0423, "be"},
+ {0x0402, "bg"},
+ {0x0403, "ca"},
+ {0x0405, "cs"},
+ {0x0406, "da"},
+ {0x0407, "de"},
+ {0x0c07, "de_AT"},
+ {0x0807, "de_CH"},
+ {0x1407, "de_LI"},
+ {0x1007, "de_LU"},
+ {0x0408, "el"},
+ {0x0409, "en"},
+ {0x0c09, "en_AU"},
+ {0x2809, "en_BZ"},
+ {0x1009, "en_CA"},
+ {0x0809, "en_GB"},
+ {0x1809, "en_IE"},
+ {0x2009, "en_JM"},
+ {0x1409, "en_NZ"},
+ {0x2c09, "en_TT"},
+ {0x0409, "en_US"},
+ {0x1c09, "en_ZA"},
+ {0x040a, "es"},
+ {0x2c0a, "es_AR"},
+ {0x400a, "es_BO"},
+ {0x340a, "es_CL"},
+ {0x240a, "es_CO"},
+ {0x140a, "es_CR"},
+ {0x1c0a, "es_DO"},
+ {0x300a, "es_EC"},
+ {0x100a, "es_GT"},
+ {0x480a, "es_HN"},
+ {0x080a, "es_MX"},
+ {0x4c0a, "es_NI"},
+ {0x180a, "es_PA"},
+ {0x280a, "es_PE"},
+ {0x500a, "es_PR"},
+ {0x3c0a, "es_PY"},
+ {0x440a, "es_SV"},
+ {0x380a, "es_UY"},
+ {0x200a, "es_VE"},
+ {0x0425, "et"},
+ {0x042d, "eu"},
+ {0x0429, "fa"},
+ {0x040b, "fi"},
+ {0x0438, "fo"},
+ {0x040c, "fr"},
+ {0x080c, "fr_BE"},
+ {0x0c0c, "fr_CA"},
+ {0x100c, "fr_CH"},
+ {0x140c, "fr_LU"},
+ {0x040d, "he"},
+ {0x0439, "hi"},
+ {0x041a, "hr"},
+ {0x040e, "hu"},
+ {0x0421, "in"},
+ {0x040f, "is"},
+ {0x0410, "it"},
+ {0x0810, "it_CH"},
+ {0x0411, "ja"},
+ {0x0812, "ko"},
+ {0x0412, "ko"},
+ {0x0427, "lt"},
+ {0x0426, "lv"},
+ {0x042f, "mk"},
+ {0x043e, "ms"},
+ {0x0458, "mt"},
+ {0x0413, "nl"},
+ {0x0813, "nl_BE"},
+ {0x0814, "no"},
+ {0x0414, "no"},
+ {0x0415, "pl"},
+ {0x0816, "pt"},
+ {0x0416, "pt_BR"},
+ {0x0418, "ro"},
+ {0x0419, "ru"},
+ {0x041c, "sq"},
+ {0x081a, "sr"},
+ {0x0c1a, "sr"},
+ {0x041d, "sv"},
+ {0x081d, "sv_FI"},
+ {0x041e, "th"},
+ {0x041f, "tr"},
+ {0x0422, "uk"},
+ {0x0420, "ur"},
+ {0x042a, "vi"},
+ {0x0804, "zh"},
+ {0x0804, "zh_CN"},
+ {0x0c04, "zh_HK"},
+ {0x1004, "zh_SG"},
+ {0x0404, "zh_TW"}
+};
+#endif
+
+#ifdef __unix__
+struct LocaleInfo {
+ char iso_name[3];
+ char int_name[32];
+};
+
+static const LocaleInfo lin_locale_info[] = {
+ {"af", "Afrikaans"},
+ {"ar", "Arabic"},
+ {"be", "Belarusian"},
+ {"bg", "Bulgarian"},
+ {"ca", "Catalan"},
+ {"cs", "Czech"},
+ {"da", "Danish"},
+ {"de", "German"},
+ {"el", "Greek"},
+ {"en", "English"},
+ {"es", "Spanish"},
+ {"et", "Estonian"},
+ {"eu", "Basque"},
+ {"fa", "Farsi"},
+ {"fi", "Finnish"},
+ {"fo", "Faroese"},
+ {"fr", "French"},
+ {"he", "Hebrew"},
+ {"hi", "Hindi"},
+ {"hr", "Croatian"},
+ {"hu", "Hungarian"},
+ {"is", "Icelandic"},
+ {"it", "Italian"},
+ {"ja", "Japanese"},
+ {"ko", "Korean"},
+ {"lt", "Lithuanian"},
+ {"lv", "Latvian"},
+ {"mk", "Macedonian"},
+ {"ms", "Malay"},
+ {"mt", "Maltese"},
+ {"nl", "Dutch"},
+ {"pl", "Polish"},
+ {"pt", "Portuguese"},
+ {"ro", "Romanian"},
+ {"ru", "Russian"},
+ {"sq", "Albanian"},
+ {"sr", "Serbian"},
+ {"sv", "Swedish"},
+ {"th", "Thai"},
+ {"tr", "Turkish"},
+ {"uk", "Ukrainian"},
+ {"ur", "Urdu"},
+ {"vi", "Vietnamese"},
+ {"zh", "Chinese"},
+};
+#endif
+
+std::string GetLocaleName(const char *code)
+{
+ if (!code)
+ return std::string();
+
+ std::string res;
+#ifdef __APPLE__
+ CFStringRef id = CFStringCreateWithCString(NULL, code, kCFStringEncodingUTF8);
+ CFLocaleRef loc = CFLocaleCreate(NULL, id);
+ if (loc) {
+ CFStringRef name = CFLocaleCopyDisplayNameForPropertyValue(loc, kCFLocaleLanguageCode, id);
+ if (name) {
+ CFMutableStringRef mutable_name = CFStringCreateMutableCopy(NULL, 0, name);
+ CFStringCapitalize(mutable_name, loc);
+ char buffer[1024];
+ if (CFStringGetCString(mutable_name, buffer, sizeof(buffer), kCFStringEncodingUTF8))
+ res = buffer;
+ CFRelease(mutable_name);
+ CFRelease(name);
+ }
+ CFRelease(loc);
+ }
+ CFRelease(id);
+#elif defined(__unix__)
+ int begin = 0;
+ int end = _countof(lin_locale_info);
+ while (end - begin > 1) {
+ int mid = (begin + end)/2;
+
+ const LocaleInfo *info = lin_locale_info + mid;
+ int cmp = strcmp(code, info->iso_name);
+ if (cmp < 0)
+ end = mid;
+ else if (cmp > 0)
+ begin = mid;
+ else {
+ res = info->int_name;
+ break;
+ }
+ }
+#else
+ LCID id = 0;
+
+ int begin = 0;
+ int end = _countof(win_locale_info);
+ while (end - begin > 1) {
+ int mid = (begin + end)/2;
+
+ const LocaleInfo *info = win_locale_info + mid;
+ int cmp = strcmp(code, info->iso_name);
+ if (cmp < 0)
+ end = mid;
+ else if (cmp > 0)
+ begin = mid;
+ else {
+ id = info->id;
+ break;
+ }
+ }
+
+ if (id) {
+ os::unicode_char buffer[1024];
+ if (GetLocaleInfoW(id, LOCALE_SNATIVELANGNAME, buffer, sizeof(buffer))) {
+ LCMapStringW(id, LCMAP_UPPERCASE, buffer, 1, buffer, 1);
+ res = os::ToUTF8(os::unicode_string(buffer));
+ }
+ }
+#endif
+ if (res.empty())
+ res = code;
+ return res;
+}
+
+std::string GetCurrentLocale()
+{
+ std::string res;
+#ifdef __APPLE__
+ CFLocaleRef loc = CFLocaleCopyCurrent();
+ if (loc) {
+ CFStringRef name = CFLocaleGetIdentifier(loc);
+ if (name) {
+ char buffer[1024];
+ if (CFStringGetCString(name, buffer, sizeof(buffer), kCFStringEncodingUTF8))
+ res = buffer;
+ }
+ CFRelease(loc);
+ }
+#elif defined(__unix__)
+ const char *lang = ::getenv("LANG");
+ if (lang && *lang)
+ {
+ while (char sym = *lang++)
+ {
+ if (sym == '.' || sym == '_')
+ break;
+ res += sym;
+ }
+ } else
+ {
+ res = "en";
+ }
+#else
+ LCID id = GetUserDefaultLCID();
+ for (size_t i = 0; i < _countof(win_locale_info); i++) {
+ if (win_locale_info[i].id == id) {
+ res = win_locale_info[i].iso_name;
+ break;
+ }
+ }
+#endif
+ return res;
+}
+
+void GetLocalTime(SYSTEM_TIME *res)
+{
+#ifdef VMP_GNU
+ time_t rawtime;
+ time(&rawtime);
+ struct tm local_tm;
+ tm *timeinfo = localtime_r(&rawtime, &local_tm);
+ res->year = static_cast<uint16_t>(timeinfo->tm_year + 1900);
+ res->month = static_cast<uint8_t>(timeinfo->tm_mon);
+ res->day = static_cast<uint8_t>(timeinfo->tm_mday);
+#else
+ SYSTEMTIME local_time;
+ ::GetLocalTime(&local_time);
+ res->year = local_time.wYear;
+ res->month = static_cast<uint8_t>(local_time.wMonth);
+ res->day = static_cast<uint8_t>(local_time.wDay);
+#endif
+}
+
+#ifndef VMP_GNU
+#include <fcntl.h>
+
+static const wchar_t letters[] =
+L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+/* Generate a temporary file name based on TMPL. TMPL must match the
+ rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed
+ does not exist at the time of the call to mkstemp. TMPL is
+ overwritten with the result. */
+int
+mkstemp (wchar_t *tmpl)
+{
+ size_t len;
+ wchar_t *XXXXXX;
+ static unsigned long long value;
+ unsigned long long random_time_bits;
+ unsigned int count;
+ int fd = -1;
+ int save_errno = errno;
+
+ /* A lower bound on the number of temporary files to attempt to
+ generate. The maximum total number of temporary file names that
+ can exist for a given template is 62**6. It should never be
+ necessary to try all these combinations. Instead if a reasonable
+ number of names is tried (we define reasonable as 62**3) fail to
+ give the system administrator the chance to remove the problems. */
+#define ATTEMPTS_MIN (62 * 62 * 62)
+
+ /* The number of times to attempt to generate a temporary file. To
+ conform to POSIX, this must be no smaller than TMP_MAX. */
+#if ATTEMPTS_MIN < TMP_MAX
+ unsigned int attempts = TMP_MAX;
+#else
+ unsigned int attempts = ATTEMPTS_MIN;
+#endif
+
+ len = wcsnlen_s (tmpl, MAX_PATH);
+ if (len < 6 || wcsncmp (&tmpl[len - 6], L"XXXXXX", MAX_PATH))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* This is where the Xs start. */
+ XXXXXX = &tmpl[len - 6];
+
+ /* Get some more or less random data. */
+ {
+ SYSTEMTIME stNow;
+ FILETIME ftNow;
+
+ // get system time
+ GetSystemTime(&stNow);
+ stNow.wMilliseconds = 500;
+ if (!SystemTimeToFileTime(&stNow, &ftNow))
+ {
+ errno = -1;
+ return -1;
+ }
+
+ random_time_bits = (((unsigned long long)ftNow.dwHighDateTime << 32)
+ | (unsigned long long)ftNow.dwLowDateTime);
+ }
+ value += random_time_bits ^ (unsigned long long)GetCurrentThreadId ();
+
+ for (count = 0; count < attempts; value += 7777, ++count)
+ {
+ unsigned long long v = value;
+
+ /* Fill in the random bits. */
+ XXXXXX[0] = letters[v % 62];
+ v /= 62;
+ XXXXXX[1] = letters[v % 62];
+ v /= 62;
+ XXXXXX[2] = letters[v % 62];
+ v /= 62;
+ XXXXXX[3] = letters[v % 62];
+ v /= 62;
+ XXXXXX[4] = letters[v % 62];
+ v /= 62;
+ XXXXXX[5] = letters[v % 62];
+
+ if (0 == _wsopen_s(&fd, tmpl, O_RDWR | O_CREAT | O_EXCL, _SH_DENYRW, _S_IREAD | _S_IWRITE) && fd >= 0)
+ {
+ errno = save_errno;
+ return fd;
+ } else if (errno != EEXIST)
+ {
+ return -1;
+ }
+ }
+
+ /* We got out of the loop because we ran out of combinations to try. */
+ errno = EEXIST;
+ return -1;
+}
+#endif
+
+std::string GetTempFilePathName(const char *pathname_template /*=NULL*/)
+{
+ std::string ret;
+#ifdef VMP_GNU
+ char szTempFileName[PATH_MAX] = "/tmp/vmpXXXXXX";
+ if(pathname_template)
+ strcpy_s(szTempFileName, pathname_template);
+ int fd = mkstemp(szTempFileName);
+ if(fd >= 0)
+ {
+ fchmod(fd, S_IRUSR | S_IWUSR | S_IXUSR);
+ ret = szTempFileName;
+ close(fd);
+ }
+#else
+ if(pathname_template)
+ {
+ os::unicode_string tempFileName = os::FromUTF8(pathname_template);
+ int fd = mkstemp(const_cast<wchar_t *>(tempFileName.data()));
+ if(fd >= 0)
+ {
+ ret = os::ToUTF8(tempFileName);
+ _close(fd);
+ }
+ } else
+ {
+ wchar_t lpTempPathBuffer[MAX_PATH], szTempFileName[MAX_PATH];
+ DWORD dwRetVal = GetTempPathW(MAX_PATH, lpTempPathBuffer);
+ if (dwRetVal <= MAX_PATH && (dwRetVal != 0))
+ {
+ UINT uRetVal = GetTempFileNameW(lpTempPathBuffer, L"vmp", 0, szTempFileName);
+ if (uRetVal > 0)
+ {
+ ret = os::ToUTF8(std::wstring(szTempFileName));
+ }
+ }
+ }
+#endif
+ return ret;
+}
+std::string GetTempFilePathNameFor(const char *pathname)
+{
+ std::string ret = os::GetTempFilePathName((std::string(pathname) + ".XXXXXX").c_str());
+ if(ret.empty())
+ {
+ return os::GetTempFilePathName();
+ }
+ return ret;
+}
+
+bool FileMove(const char *oldName, const char *newName)
+{
+#ifdef VMP_GNU
+ return rename(oldName, newName) == 0;
+#else
+ return MoveFileExW(os::FromUTF8(oldName).c_str(), os::FromUTF8(newName).c_str(),
+ MOVEFILE_COPY_ALLOWED | // used only when different volumes
+ MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == TRUE;
+#endif
+}
+
+#ifdef __APPLE__
+std::string GetMainExeFileName(const char *file_name)
+{
+ std::string res;
+ CFStringRef cfPath = CFStringCreateWithCString(NULL, file_name, kCFStringEncodingUTF8);
+ if (cfPath) {
+ CFURLRef bundleURL = CFURLCreateWithFileSystemPath(NULL, cfPath, kCFURLPOSIXPathStyle, true);
+ if (bundleURL) {
+ CFBundleRef aBundle = CFBundleCreate(NULL, bundleURL);
+ if (aBundle) {
+ CFURLRef mainExecUrl = CFBundleCopyExecutableURL(aBundle);
+ if(mainExecUrl) {
+ CFURLRef mainExecAbsUrl = CFURLCopyAbsoluteURL(mainExecUrl);
+ if(mainExecAbsUrl) {
+ CFStringRef mainExec = CFURLCopyFileSystemPath(mainExecAbsUrl, kCFURLPOSIXPathStyle);
+ if (mainExec) {
+ char buffer[PATH_MAX];
+ if (CFStringGetCString(mainExec, buffer, sizeof(buffer), kCFStringEncodingUTF8))
+ res = buffer;
+ CFRelease(mainExec);
+ }
+ CFRelease(mainExecAbsUrl);
+ }
+ CFRelease(mainExecUrl);
+ }
+ CFRelease(aBundle);
+ }
+ CFRelease(bundleURL);
+ }
+ CFRelease(cfPath);
+ }
+ return res;
+}
+#endif
+
+std::string CombineThisAppDataDirectory(const char *lastPathPart)
+{
+ return os::CombinePaths(os::CombinePaths(os::GetSysAppDataDirectory().c_str(), "VMProtect Software/VMProtect").c_str(), lastPathPart);
+}
+
+HMODULE LibraryOpen(const std::string &name)
+{
+#ifdef VMP_GNU
+ return dlopen(name.c_str(), RTLD_NOW);
+#else
+ return LoadLibraryW(FromUTF8(name).c_str());
+#endif
+}
+
+bool LibraryClose(HMODULE h)
+{
+#ifdef VMP_GNU
+ return (dlclose(h) == 0);
+#else
+ return (FreeLibrary(h) != 0);
+#endif
+}
+
+void *GetFunction(HMODULE h, const std::string &name)
+{
+#ifdef VMP_GNU
+ return dlsym(h, name.c_str());
+#else
+ return GetProcAddress(h, name.c_str());
+#endif
+}
+
+std::string ExpandEnvironmentVariables(const char *path)
+{
+#ifdef VMP_GNU
+ std::string res;
+ for (const char *p = path; *p; ) {
+ const char *first = strchr(p, '%');
+ if (first) {
+ const char *next = strchr(first + 1, '%');
+ if (next) {
+ const char *var_value = getenv(std::string(first + 1, next - first - 1).c_str());
+ if (var_value) {
+ res.append(p, first - p);
+ res.append(var_value);
+ } else {
+ res.append(p, next + 1 - p);
+ }
+ p = next + 1;
+ continue;
+ }
+ }
+
+ res.append(p);
+ break;
+ }
+ return res;
+#else
+ unicode_string src = FromUTF8(path);
+ unicode_string res;
+ for (const unicode_char *p = src.c_str(); *p; ) {
+ const unicode_char *first = wcschr(p, '%');
+ if (first) {
+ const unicode_char *next = wcschr(first + 1, '%');
+ if (next) {
+ size_t var_size;
+ unicode_string var = unicode_string(first + 1, next - first - 1);
+ _wgetenv_s(&var_size, NULL, 0, var.c_str()); //-V530
+ if (var_size) {
+ res.append(p, first - p);
+ unicode_char *var_value = new unicode_char[var_size];
+ _wgetenv_s(&var_size, var_value, var_size, var.c_str()); //-V530
+ res.append(var_value);
+ delete [] var_value;
+ } else {
+ res.append(p, next + 1 - p);
+ }
+ p = next + 1;
+ continue;
+ }
+ }
+
+ res.append(p);
+ break;
+ }
+ return ToUTF8(res);
+#endif
+}
+
+std::string GetEnvironmentVariable(const char *name)
+{
+#ifdef VMP_GNU
+ if (const char *p = getenv(name))
+ return std::string(p);
+ return std::string();
+#else
+ unicode_string var = FromUTF8(name);
+ unicode_string res;
+ size_t var_size;
+ _wgetenv_s(&var_size, NULL, 0, var.c_str());
+ if (var_size) {
+ unicode_char *var_value = new unicode_char[var_size];
+ _wgetenv_s(&var_size, var_value, var_size, var.c_str()); //-V530
+ res = var_value;
+ delete[] var_value;
+ }
+ return ToUTF8(res);
+#endif
+}
+
+void SetEnvironmentVariable(const char *name, const char *value)
+{
+#ifdef VMP_GNU
+ setenv(name, value, 1);
+#else
+ _wputenv_s(FromUTF8(name).c_str(), FromUTF8(value).c_str());
+#endif
+}
+
+}