From f23224bbcc45768efcbf2777bd2b953c0d821322 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Fri, 26 Sep 2025 17:38:24 -0600 Subject: [PATCH] cl.exe works! but I didn't review most of this code --- CMakeLists.txt | 1 + common.h | 2 + dll/advapi32.cpp | 545 +++++++++++++++++++++++++++++++ dll/kernel32.cpp | 769 +++++++++++++++++++++++++++++++++++++++----- dll/psapi.cpp | 67 ++++ dll/rpcrt4.cpp | 295 +++++++++++++++++ handles.h | 6 +- loader.cpp | 54 +++- module_registry.cpp | 3 +- 9 files changed, 1658 insertions(+), 84 deletions(-) create mode 100644 dll/psapi.cpp create mode 100644 dll/rpcrt4.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a9dd13..f377820 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable(wibo dll/mscoree.cpp dll/msvcrt.cpp dll/ntdll.cpp + dll/rpcrt4.cpp dll/ole32.cpp dll/user32.cpp dll/vcruntime.cpp diff --git a/common.h b/common.h index d1eec02..fc7bffc 100644 --- a/common.h +++ b/common.h @@ -64,6 +64,7 @@ typedef unsigned char BYTE; #define ERROR_INVALID_PARAMETER 87 #define ERROR_BUFFER_OVERFLOW 111 #define ERROR_INSUFFICIENT_BUFFER 122 +#define ERROR_NONE_MAPPED 1332 #define ERROR_RESOURCE_DATA_NOT_FOUND 1812 #define ERROR_RESOURCE_TYPE_NOT_FOUND 1813 #define ERROR_RESOURCE_NAME_NOT_FOUND 1814 @@ -72,6 +73,7 @@ typedef unsigned char BYTE; #define ERROR_NEGATIVE_SEEK 131 #define ERROR_BAD_EXE_FORMAT 193 #define ERROR_ALREADY_EXISTS 183 +#define ERROR_NOT_OWNER 288 #define INVALID_SET_FILE_POINTER ((DWORD)-1) #define INVALID_HANDLE_VALUE ((HANDLE)-1) diff --git a/dll/advapi32.cpp b/dll/advapi32.cpp index b3052c2..d74bbee 100644 --- a/dll/advapi32.cpp +++ b/dll/advapi32.cpp @@ -1,5 +1,287 @@ #include "common.h" +#include "handles.h" #include +#include +#include + +namespace { + using ALG_ID = unsigned int; + + constexpr ALG_ID CALG_MD5 = 0x00008003; + constexpr ALG_ID CALG_SHA1 = 0x00008004; + + constexpr DWORD HP_ALGID = 0x00000001; + constexpr DWORD HP_HASHVAL = 0x00000002; + constexpr DWORD HP_HASHSIZE = 0x00000004; + + struct HashObject { + ALG_ID algid = 0; + std::vector data; + std::vector digest; + bool digestComputed = false; + }; + + struct TokenObject { + HANDLE processHandle = nullptr; + DWORD desiredAccess = 0; + }; + + struct SidIdentifierAuthority { + uint8_t Value[6] = {0}; + }; + + struct Sid { + uint8_t Revision = 1; + uint8_t SubAuthorityCount = 0; + SidIdentifierAuthority IdentifierAuthority = {}; + uint32_t SubAuthority[1] = {0}; + }; + + struct SidAndAttributes { + Sid *SidPtr = nullptr; + DWORD Attributes = 0; + }; + + struct TokenUserData { + SidAndAttributes User; + }; + + enum SID_NAME_USE { + SidTypeUser = 1, + SidTypeGroup, + SidTypeDomain, + SidTypeAlias, + SidTypeWellKnownGroup, + SidTypeDeletedAccount, + SidTypeInvalid, + SidTypeUnknown, + SidTypeComputer, + SidTypeLabel + }; + + bool isLocalSystemSid(const Sid *sid) { + if (!sid) { + return false; + } + static const uint8_t ntAuthority[6] = {0, 0, 0, 0, 0, 5}; + if (sid->Revision != 1 || sid->SubAuthorityCount != 1) { + return false; + } + for (size_t i = 0; i < 6; ++i) { + if (sid->IdentifierAuthority.Value[i] != ntAuthority[i]) { + return false; + } + } + return sid->SubAuthority[0] == 18; // SECURITY_LOCAL_SYSTEM_RID + } + + struct Luid { + uint32_t LowPart = 0; + int32_t HighPart = 0; + }; + + struct TokenStatisticsData { + Luid tokenId; + Luid authenticationId; + int64_t expirationTime = 0; + uint32_t tokenType = 0; + uint32_t impersonationLevel = 0; + uint32_t dynamicCharged = 0; + uint32_t dynamicAvailable = 0; + uint32_t groupCount = 0; + uint32_t privilegeCount = 0; + Luid modifiedId; + }; + + static inline uint32_t leftRotate(uint32_t value, uint32_t bits) { + return (value << bits) | (value >> (32 - bits)); + } + + static std::vector computeMD5(const std::vector &input) { + static const uint32_t s[64] = { + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 + }; + static const uint32_t K[64] = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 + }; + + std::vector data = input; + uint64_t bitLen = static_cast(data.size()) * 8ULL; + data.push_back(0x80); + while ((data.size() % 64) != 56) { + data.push_back(0); + } + for (int i = 0; i < 8; ++i) { + data.push_back(static_cast((bitLen >> (8 * i)) & 0xFF)); + } + + uint32_t A = 0x67452301; + uint32_t B = 0xEFCDAB89; + uint32_t C = 0x98BADCFE; + uint32_t D = 0x10325476; + + for (size_t offset = 0; offset < data.size(); offset += 64) { + uint32_t M[16]; + for (int i = 0; i < 16; ++i) { + M[i] = static_cast(data[offset + i * 4]) | + (static_cast(data[offset + i * 4 + 1]) << 8) | + (static_cast(data[offset + i * 4 + 2]) << 16) | + (static_cast(data[offset + i * 4 + 3]) << 24); + } + uint32_t a = A; + uint32_t b = B; + uint32_t c = C; + uint32_t d = D; + for (int i = 0; i < 64; ++i) { + uint32_t F; + int g; + if (i < 16) { + F = (b & c) | ((~b) & d); + g = i; + } else if (i < 32) { + F = (d & b) | ((~d) & c); + g = (5 * i + 1) % 16; + } else if (i < 48) { + F = b ^ c ^ d; + g = (3 * i + 5) % 16; + } else { + F = c ^ (b | (~d)); + g = (7 * i) % 16; + } + uint32_t temp = d; + d = c; + c = b; + uint32_t rotateVal = a + F + K[i] + M[g]; + b = b + leftRotate(rotateVal, s[i]); + a = temp; + } + A += a; + B += b; + C += c; + D += d; + } + + std::vector digest(16); + uint32_t output[4] = {A, B, C, D}; + for (int i = 0; i < 4; ++i) { + digest[i * 4] = static_cast(output[i] & 0xFF); + digest[i * 4 + 1] = static_cast((output[i] >> 8) & 0xFF); + digest[i * 4 + 2] = static_cast((output[i] >> 16) & 0xFF); + digest[i * 4 + 3] = static_cast((output[i] >> 24) & 0xFF); + } + return digest; + } + + static std::vector computeSHA1(const std::vector &input) { + std::vector data = input; + uint64_t bitLen = static_cast(data.size()) * 8ULL; + data.push_back(0x80); + while ((data.size() % 64) != 56) { + data.push_back(0); + } + for (int i = 7; i >= 0; --i) { + data.push_back(static_cast((bitLen >> (8 * i)) & 0xFF)); + } + + uint32_t h0 = 0x67452301; + uint32_t h1 = 0xEFCDAB89; + uint32_t h2 = 0x98BADCFE; + uint32_t h3 = 0x10325476; + uint32_t h4 = 0xC3D2E1F0; + + for (size_t offset = 0; offset < data.size(); offset += 64) { + uint32_t w[80]; + for (int i = 0; i < 16; ++i) { + w[i] = (static_cast(data[offset + i * 4]) << 24) | + (static_cast(data[offset + i * 4 + 1]) << 16) | + (static_cast(data[offset + i * 4 + 2]) << 8) | + static_cast(data[offset + i * 4 + 3]); + } + for (int i = 16; i < 80; ++i) { + w[i] = leftRotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); + } + uint32_t a = h0; + uint32_t b = h1; + uint32_t c = h2; + uint32_t d = h3; + uint32_t e = h4; + for (int i = 0; i < 80; ++i) { + uint32_t f; + uint32_t k; + if (i < 20) { + f = (b & c) | ((~b) & d); + k = 0x5A827999; + } else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + uint32_t temp = leftRotate(a, 5) + f + e + k + w[i]; + e = d; + d = c; + c = leftRotate(b, 30); + b = a; + a = temp; + } + h0 += a; + h1 += b; + h2 += c; + h3 += d; + h4 += e; + } + + std::vector digest(20); + uint32_t output[5] = {h0, h1, h2, h3, h4}; + for (int i = 0; i < 5; ++i) { + digest[i * 4] = static_cast((output[i] >> 24) & 0xFF); + digest[i * 4 + 1] = static_cast((output[i] >> 16) & 0xFF); + digest[i * 4 + 2] = static_cast((output[i] >> 8) & 0xFF); + digest[i * 4 + 3] = static_cast(output[i] & 0xFF); + } + return digest; + } + + static bool computeDigest(HashObject &hash) { + if (hash.digestComputed) { + return true; + } + switch (hash.algid) { + case CALG_MD5: + hash.digest = computeMD5(hash.data); + hash.digestComputed = true; + return true; + case CALG_SHA1: + hash.digest = computeSHA1(hash.data); + hash.digestComputed = true; + return true; + default: + return false; + } + } +} namespace advapi32 { unsigned int WIN_FUNC RegOpenKeyExA(void *hKey, const char *lpSubKey, unsigned int ulOptions, void *samDesired, void **phkResult) { @@ -36,6 +318,262 @@ namespace advapi32 { return TRUE; } + + BOOL WIN_FUNC CryptCreateHash(void* hProv, unsigned int Algid, void* hKey, unsigned int dwFlags, void** phHash) { + DEBUG_LOG("CryptCreateHash(Algid=0x%x)\n", Algid); + (void)hProv; + if (!phHash) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + if (dwFlags != 0) { + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + if (hKey != nullptr) { + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + if (Algid != CALG_MD5 && Algid != CALG_SHA1) { + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + auto *hash = new HashObject; + hash->algid = Algid; + hash->digestComputed = false; + hash->data.clear(); + hash->digest.clear(); + *phHash = hash; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + BOOL WIN_FUNC CryptHashData(void* hHash, const unsigned char* pbData, unsigned int dwDataLen, unsigned int dwFlags) { + DEBUG_LOG("CryptHashData(%p, %u bytes)\n", hHash, dwDataLen); + if (!hHash || (dwDataLen && !pbData) || dwFlags != 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + auto *hash = reinterpret_cast(hHash); + if (pbData && dwDataLen) { + hash->data.insert(hash->data.end(), pbData, pbData + dwDataLen); + hash->digestComputed = false; + hash->digest.clear(); + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + BOOL WIN_FUNC CryptGetHashParam(void* hHash, unsigned int dwParam, unsigned char* pbData, unsigned int* pdwDataLen, unsigned int dwFlags) { + DEBUG_LOG("CryptGetHashParam(%p, param=0x%x)\n", hHash, dwParam); + if (!hHash || !pdwDataLen || dwFlags != 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + auto *hash = reinterpret_cast(hHash); + switch (dwParam) { + case HP_ALGID: { + unsigned int required = sizeof(ALG_ID); + if (!pbData) { + *pdwDataLen = required; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + if (*pdwDataLen < required) { + *pdwDataLen = required; + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return FALSE; + } + memcpy(pbData, &hash->algid, required); + *pdwDataLen = required; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + case HP_HASHSIZE: { + unsigned int size = 0; + switch (hash->algid) { + case CALG_MD5: + size = 16; + break; + case CALG_SHA1: + size = 20; + break; + default: + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + if (!pbData) { + *pdwDataLen = sizeof(unsigned int); + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + if (*pdwDataLen < sizeof(unsigned int)) { + *pdwDataLen = sizeof(unsigned int); + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return FALSE; + } + memcpy(pbData, &size, sizeof(unsigned int)); + *pdwDataLen = sizeof(unsigned int); + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + case HP_HASHVAL: { + if (!computeDigest(*hash)) { + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + unsigned int required = hash->digest.size(); + if (!pbData) { + *pdwDataLen = required; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + if (*pdwDataLen < required) { + *pdwDataLen = required; + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return FALSE; + } + memcpy(pbData, hash->digest.data(), required); + *pdwDataLen = required; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + default: + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + } + + BOOL WIN_FUNC CryptDestroyHash(void* hHash) { + DEBUG_LOG("CryptDestroyHash(%p)\n", hHash); + if (!hHash) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + delete reinterpret_cast(hHash); + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + BOOL WIN_FUNC OpenProcessToken(HANDLE ProcessHandle, DWORD DesiredAccess, HANDLE *TokenHandle) { + DEBUG_LOG("OpenProcessToken(process=%p, access=0x%x)\n", ProcessHandle, DesiredAccess); + if (!TokenHandle) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + auto *token = new TokenObject; + token->processHandle = ProcessHandle; + token->desiredAccess = DesiredAccess; + handles::Data data; + data.type = handles::TYPE_TOKEN; + data.ptr = token; + data.size = 0; + *TokenHandle = handles::allocDataHandle(data); + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + void releaseToken(void *tokenPtr) { + delete reinterpret_cast(tokenPtr); + } + + BOOL WIN_FUNC GetTokenInformation(HANDLE TokenHandle, unsigned int TokenInformationClass, void *TokenInformation, unsigned int TokenInformationLength, unsigned int *ReturnLength) { + DEBUG_LOG("GetTokenInformation(%p, class=%u, len=%u)\n", TokenHandle, TokenInformationClass, TokenInformationLength); + if (!ReturnLength) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + auto data = handles::dataFromHandle(TokenHandle, false); + if (data.type != handles::TYPE_TOKEN) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + constexpr unsigned int TokenUserClass = 1; // TokenUser + constexpr unsigned int TokenStatisticsClass = 10; // TokenStatistics + constexpr unsigned int TokenElevationClass = 20; // TokenElevation + if (TokenInformationClass == TokenUserClass) { + constexpr size_t sidSize = sizeof(Sid); + constexpr size_t tokenUserSize = sizeof(TokenUserData); + const unsigned int required = static_cast(tokenUserSize + sidSize); + *ReturnLength = required; + if (!TokenInformation || TokenInformationLength < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return FALSE; + } + auto *tokenUser = reinterpret_cast(TokenInformation); + auto *sid = reinterpret_cast(reinterpret_cast(TokenInformation) + tokenUserSize); + SidIdentifierAuthority ntAuthority = {{0, 0, 0, 0, 0, 5}}; + sid->Revision = 1; + sid->SubAuthorityCount = 1; + sid->IdentifierAuthority = ntAuthority; + sid->SubAuthority[0] = 18; // SECURITY_LOCAL_SYSTEM_RID + tokenUser->User.SidPtr = sid; + tokenUser->User.Attributes = 0; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + if (TokenInformationClass == TokenStatisticsClass) { + const unsigned int required = sizeof(TokenStatisticsData); + *ReturnLength = required; + if (!TokenInformation || TokenInformationLength < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return FALSE; + } + auto *stats = reinterpret_cast(TokenInformation); + memset(stats, 0, required); + stats->tokenType = 1; // TokenPrimary + stats->impersonationLevel = 0; // SecurityAnonymous + stats->tokenId.LowPart = 1; + stats->authenticationId.LowPart = 1; + stats->modifiedId.LowPart = 1; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + if (TokenInformationClass == TokenElevationClass) { + const unsigned int required = sizeof(DWORD); + *ReturnLength = required; + if (!TokenInformation || TokenInformationLength < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return FALSE; + } + *reinterpret_cast(TokenInformation) = 0; // not elevated + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + + BOOL WIN_FUNC LookupAccountSidW(const wchar_t *lpSystemName, const void *sidPointer, wchar_t *Name, unsigned long *cchName, wchar_t *ReferencedDomainName, unsigned long *cchReferencedDomainName, SID_NAME_USE *peUse) { + DEBUG_LOG("LookupAccountSidW(system=%ls, sid=%p)\n", lpSystemName ? lpSystemName : L"(null)", sidPointer); + (void) lpSystemName; // Only local lookup supported + if (!sidPointer || !cchName || !cchReferencedDomainName || !peUse) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + auto *sid = reinterpret_cast(sidPointer); + if (!isLocalSystemSid(sid)) { + wibo::lastError = ERROR_NONE_MAPPED; + return FALSE; + } + const wchar_t *accountName = L"SYSTEM"; + const wchar_t *domainName = L"NT AUTHORITY"; + unsigned long requiredAccount = static_cast(std::wcslen(accountName) + 1); + unsigned long requiredDomain = static_cast(std::wcslen(domainName) + 1); + if (!Name || *cchName < requiredAccount || !ReferencedDomainName || *cchReferencedDomainName < requiredDomain) { + *cchName = requiredAccount; + *cchReferencedDomainName = requiredDomain; + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return FALSE; + } + std::wmemcpy(Name, accountName, requiredAccount); + std::wmemcpy(ReferencedDomainName, domainName, requiredDomain); + *peUse = SidTypeWellKnownGroup; + *cchName = requiredAccount - 1; + *cchReferencedDomainName = requiredDomain - 1; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } } static void *resolveByName(const char *name) { @@ -43,6 +581,13 @@ static void *resolveByName(const char *name) { if (strcmp(name, "CryptReleaseContext") == 0) return (void*) advapi32::CryptReleaseContext; if (strcmp(name, "CryptAcquireContextW") == 0) return (void*) advapi32::CryptAcquireContextW; if (strcmp(name, "CryptGenRandom") == 0) return (void*) advapi32::CryptGenRandom; + if (strcmp(name, "CryptCreateHash") == 0) return (void*) advapi32::CryptCreateHash; + if (strcmp(name, "CryptHashData") == 0) return (void*) advapi32::CryptHashData; + if (strcmp(name, "CryptGetHashParam") == 0) return (void*) advapi32::CryptGetHashParam; + if (strcmp(name, "CryptDestroyHash") == 0) return (void*) advapi32::CryptDestroyHash; + if (strcmp(name, "OpenProcessToken") == 0) return (void*) advapi32::OpenProcessToken; + if (strcmp(name, "GetTokenInformation") == 0) return (void*) advapi32::GetTokenInformation; + if (strcmp(name, "LookupAccountSidW") == 0) return (void*) advapi32::LookupAccountSidW; return nullptr; } diff --git a/dll/kernel32.cpp b/dll/kernel32.cpp index ba3409d..a7434a5 100644 --- a/dll/kernel32.cpp +++ b/dll/kernel32.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -26,8 +28,56 @@ #include #include #include +#include +#include +#include +#include + +namespace advapi32 { + void releaseToken(void *tokenPtr); +} namespace { + struct MappingObject; + struct ViewInfo { + void *mapBase = nullptr; + size_t mapLength = 0; + MappingObject *owner = nullptr; + }; + + struct MappingObject { + int fd = -1; + size_t maxSize = 0; + unsigned int protect = 0; + bool anonymous = false; + bool closed = false; + size_t refCount = 0; + }; + + void closeMappingIfPossible(MappingObject *mapping); + void tryReleaseMapping(MappingObject *mapping); + std::unordered_map g_viewInfo; + + void closeMappingIfPossible(MappingObject *mapping) { + if (!mapping) { + return; + } + if (mapping->fd != -1) { + close(mapping->fd); + mapping->fd = -1; + } + delete mapping; + } + + void tryReleaseMapping(MappingObject *mapping) { + if (!mapping) { + return; + } + if (mapping->closed && mapping->refCount == 0) { + closeMappingIfPossible(mapping); + } + } + using DWORD_PTR = uintptr_t; constexpr WORD PROCESSOR_ARCHITECTURE_INTEL = 0; @@ -107,6 +157,44 @@ namespace kernel32 { return ret; } + struct MutexObject { + pthread_mutex_t mutex; + bool ownerValid = false; + pthread_t owner = 0; + unsigned int recursionCount = 0; + std::u16string name; + int refCount = 1; + }; + + static std::mutex mutexRegistryLock; + static std::unordered_map namedMutexes; + + static std::u16string makeMutexName(LPCWSTR name) { + if (!name) { + return std::u16string(); + } + size_t len = wstrlen(reinterpret_cast(name)); + return std::u16string(reinterpret_cast(name), len); + } + + static void releaseMutexObject(MutexObject *obj) { + if (!obj) { + return; + } + std::lock_guard lock(mutexRegistryLock); + obj->refCount--; + if (obj->refCount == 0) { + if (!obj->name.empty()) { + auto it = namedMutexes.find(obj->name); + if (it != namedMutexes.end() && it->second == obj) { + namedMutexes.erase(it); + } + } + pthread_mutex_destroy(&obj->mutex); + delete obj; + } + } + static int doCompareString(const std::string &a, const std::string &b, unsigned int dwCmpFlags) { for (size_t i = 0; ; i++) { if (i == a.size()) { @@ -173,6 +261,28 @@ namespace kernel32 { wibo::lastError = dwErrCode; } + BOOL WIN_FUNC Wow64DisableWow64FsRedirection(void **OldValue) { + DEBUG_LOG("Wow64DisableWow64FsRedirection\n"); + if (OldValue) { + *OldValue = nullptr; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + BOOL WIN_FUNC Wow64RevertWow64FsRedirection(void *OldValue) { + DEBUG_LOG("Wow64RevertWow64FsRedirection\n"); + (void) OldValue; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + void WIN_FUNC RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, const ULONG_PTR *lpArguments) { + DEBUG_LOG("RaiseException(code=0x%x, flags=0x%x, args=%u)\n", dwExceptionCode, dwExceptionFlags, nNumberOfArguments); + (void)lpArguments; + exit(static_cast(dwExceptionCode)); + } + PVOID WIN_FUNC AddVectoredExceptionHandler(ULONG first, PVECTORED_EXCEPTION_HANDLER handler) { DEBUG_LOG("STUB: AddVectoredExceptionHandler(%u, %p)\n", first, handler); return (PVOID)handler; @@ -374,30 +484,107 @@ namespace kernel32 { return 1; } + BOOL WIN_FUNC CreateProcessW( + LPCWSTR lpApplicationName, + LPWSTR lpCommandLine, + void *lpProcessAttributes, + void *lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + void *lpStartupInfo, + PROCESS_INFORMATION *lpProcessInformation + ) { + std::string applicationUtf8; + if (lpApplicationName) { + applicationUtf8 = wideStringToString(lpApplicationName); + } + std::string commandUtf8; + if (lpCommandLine) { + commandUtf8 = wideStringToString(lpCommandLine); + } + std::string directoryUtf8; + if (lpCurrentDirectory) { + directoryUtf8 = wideStringToString(lpCurrentDirectory); + } + DEBUG_LOG("CreateProcessW %s \"%s\" %p %p %d 0x%x %p %s %p %p\n", + applicationUtf8.empty() ? "" : applicationUtf8.c_str(), + commandUtf8.empty() ? "" : commandUtf8.c_str(), + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags, + lpEnvironment, + directoryUtf8.empty() ? "" : directoryUtf8.c_str(), + lpStartupInfo, + lpProcessInformation + ); + std::vector commandBuffer; + if (!commandUtf8.empty()) { + commandBuffer.assign(commandUtf8.begin(), commandUtf8.end()); + commandBuffer.push_back('\0'); + } + LPSTR commandPtr = commandBuffer.empty() ? nullptr : commandBuffer.data(); + LPCSTR applicationPtr = applicationUtf8.empty() ? nullptr : applicationUtf8.c_str(); + LPCSTR directoryPtr = directoryUtf8.empty() ? nullptr : directoryUtf8.c_str(); + return CreateProcessA( + applicationPtr, + commandPtr, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags, + lpEnvironment, + directoryPtr, + lpStartupInfo, + lpProcessInformation + ); + } + unsigned int WIN_FUNC WaitForSingleObject(void *hHandle, unsigned int dwMilliseconds) { DEBUG_LOG("WaitForSingleObject (%u)\n", dwMilliseconds); - - // TODO - wait on other objects? - - // TODO: wait for less than forever - assert(dwMilliseconds == 0xffffffff); - - processes::Process* process = processes::processFromHandle(hHandle, false); - - int status; - waitpid(process->pid, &status, 0); - - if (WIFEXITED(status)) { - process->exitCode = WEXITSTATUS(status); - } else { - // If we're here, *something* has caused our child process to exit abnormally - // Specific exit codes don't really map onto any of these situations - we just know it's bad. - // Specify a non-zero exit code to alert our parent process something's gone wrong. - DEBUG_LOG("WaitForSingleObject: Child process exited abnormally - returning exit code 1."); - process->exitCode = 1; + handles::Data data = handles::dataFromHandle(hHandle, false); + switch (data.type) { + case handles::TYPE_PROCESS: { + // TODO: wait for less than forever + assert(dwMilliseconds == 0xffffffff); + processes::Process *process = reinterpret_cast(data.ptr); + int status; + waitpid(process->pid, &status, 0); + if (WIFEXITED(status)) { + process->exitCode = WEXITSTATUS(status); + } else { + DEBUG_LOG("WaitForSingleObject: Child process exited abnormally - returning exit code 1.\n"); + process->exitCode = 1; + } + wibo::lastError = ERROR_SUCCESS; + return 0; + } + case handles::TYPE_MUTEX: { + MutexObject *obj = reinterpret_cast(data.ptr); + if (dwMilliseconds != 0xffffffff) { + DEBUG_LOG("WaitForSingleObject: timeout for mutex not supported\n"); + wibo::lastError = ERROR_NOT_SUPPORTED; + return 0xFFFFFFFF; + } + pthread_mutex_lock(&obj->mutex); + pthread_t self = pthread_self(); + if (obj->ownerValid && pthread_equal(obj->owner, self)) { + obj->recursionCount++; + } else { + obj->owner = self; + obj->ownerValid = true; + obj->recursionCount = 1; + } + wibo::lastError = ERROR_SUCCESS; + return 0; + } + default: + DEBUG_LOG("WaitForSingleObject: unsupported handle type %d\n", data.type); + wibo::lastError = ERROR_INVALID_HANDLE; + return 0xFFFFFFFF; } - - return 0; } int WIN_FUNC GetSystemDefaultLangID() { @@ -680,12 +867,18 @@ namespace kernel32 { if (!(fp == stdin || fp == stdout || fp == stderr)) { fclose(fp); } - } else if (data.type == handles::TYPE_MAPPED) { - if (data.ptr != (void *) 0x1) { - munmap(data.ptr, data.size); - } - } else if (data.type == handles::TYPE_PROCESS) { + } else if (data.type == handles::TYPE_MAPPED) { + auto *mapping = reinterpret_cast(data.ptr); + if (mapping) { + mapping->closed = true; + tryReleaseMapping(mapping); + } + } else if (data.type == handles::TYPE_PROCESS) { delete (processes::Process*) data.ptr; + } else if (data.type == handles::TYPE_TOKEN) { + advapi32::releaseToken(data.ptr); + } else if (data.type == handles::TYPE_MUTEX) { + releaseMutexObject(reinterpret_cast(data.ptr)); } return TRUE; } @@ -717,21 +910,37 @@ namespace kernel32 { } DWORD WIN_FUNC GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR *lpFilePart) { - const auto fileName = wideStringToString(lpFileName); - DEBUG_LOG("GetFullPathNameW(%s) ", fileName.c_str()); + std::string narrowName = wideStringToString(lpFileName); + DEBUG_LOG("GetFullPathNameW(%s) ", narrowName.c_str()); - const auto lpFileNameA = wideStringToString(lpFileName); - std::filesystem::path absPath = std::filesystem::absolute(files::pathFromWindows(lpFileNameA.c_str())); + std::filesystem::path absPath = std::filesystem::absolute(files::pathFromWindows(narrowName.c_str())); std::string absStr = files::pathToWindows(absPath); - const auto absStrW = stringToWideString(absStr.c_str()); + auto absStrW = stringToWideString(absStr.c_str()); DEBUG_LOG("-> %s\n", absStr.c_str()); - const auto len = wstrlen(absStrW.data()); - if (nBufferLength < len + 1) { + size_t len = wstrlen(absStrW.data()); + if (nBufferLength == 0 || nBufferLength <= len) { + if (lpFilePart) { + *lpFilePart = nullptr; + } return len + 1; } + wstrncpy(lpBuffer, absStrW.data(), len + 1); - assert(!lpFilePart); + if (lpFilePart) { + *lpFilePart = nullptr; + std::error_code ec; + bool pathIsDir = std::filesystem::is_directory(absPath, ec) && !ec; + if (!pathIsDir) { + uint16_t *lastSlash = wstrrchr(lpBuffer, '\\'); + if (lastSlash && *(lastSlash + 1) != 0) { + *lpFilePart = lastSlash + 1; + } else if (!lastSlash && len > 0) { + *lpFilePart = lpBuffer; + } + } + } + wibo::lastError = ERROR_SUCCESS; return len; } @@ -759,6 +968,21 @@ namespace kernel32 { } } + DWORD WIN_FUNC GetShortPathNameW(LPCWSTR lpszLongPath, LPWSTR lpszShortPath, DWORD cchBuffer) { + std::string longPath = wideStringToString(lpszLongPath); + DEBUG_LOG("GetShortPathNameW(%s)\n", longPath.c_str()); + std::filesystem::path absPath = std::filesystem::absolute(files::pathFromWindows(longPath.c_str())); + std::string absStr = files::pathToWindows(absPath); + auto absStrW = stringToWideString(absStr.c_str()); + size_t len = wstrlen(absStrW.data()); + if (cchBuffer == 0 || cchBuffer <= len) { + return len + 1; + } + wstrncpy(lpszShortPath, absStrW.data(), len + 1); + wibo::lastError = ERROR_SUCCESS; + return len; + } + using random_shorts_engine = std::independent_bits_engine; unsigned int WIN_FUNC GetTempFileNameA(LPSTR lpPathName, LPSTR lpPrefixString, unsigned int uUnique, LPSTR lpTempFileName) { @@ -826,6 +1050,29 @@ namespace kernel32 { (unsigned int)(UNIX_TIME_ZERO >> 32) }; + static FILETIME fileTimeFromDuration(uint64_t ticks100ns) { + FILETIME result; + result.dwLowDateTime = (unsigned int)(ticks100ns & 0xFFFFFFFF); + result.dwHighDateTime = (unsigned int)(ticks100ns >> 32); + return result; + } + + static FILETIME fileTimeFromTimeval(const struct timeval &value) { + uint64_t total = 0; + if (value.tv_sec > 0 || value.tv_usec > 0) { + total = (uint64_t)value.tv_sec * 10000000ULL + (uint64_t)value.tv_usec * 10ULL; + } + return fileTimeFromDuration(total); + } + + static FILETIME fileTimeFromTimespec(const struct timespec &value) { + uint64_t total = 0; + if (value.tv_sec > 0 || value.tv_nsec > 0) { + total = (uint64_t)value.tv_sec * 10000000ULL + (uint64_t)value.tv_nsec / 100ULL; + } + return fileTimeFromDuration(total); + } + template struct WIN32_FIND_DATA { uint32_t dwFileAttributes; @@ -1227,62 +1474,219 @@ namespace kernel32 { unsigned int dwMaximumSizeHigh, unsigned int dwMaximumSizeLow, const char *lpName) { - DEBUG_LOG("CreateFileMappingA(%p, %p, %u, %u, %u, %s)\n", hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpName); + DEBUG_LOG("CreateFileMappingA(%p, %p, %u, %u, %u, %s)\n", hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpName ? lpName : "(null)"); + (void) lpFileMappingAttributes; + (void) lpName; - int64_t size = (int64_t) dwMaximumSizeHigh << 32 | dwMaximumSizeLow; + auto mapping = new MappingObject(); + mapping->protect = flProtect; - void *mmapped; - - if (hFile == (void*) -1) { // INVALID_HANDLE_VALUE - if (size == 0) { - mmapped = (void *) 0x1; - } else { - mmapped = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - } - } else { - int fd = fileno(files::fpFromHandle(hFile)); - - if (size == 0) { - size = getFileSize(hFile); - if (size == -1) { - return (void*) -1; - } - } - - if (size == 0) { - mmapped = (void *) 0x1; - } else { - mmapped = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - } + uint64_t size = ((uint64_t) dwMaximumSizeHigh << 32) | dwMaximumSizeLow; + if (flProtect != 0x02 /* PAGE_READONLY */ && flProtect != 0x04 /* PAGE_READWRITE */ && flProtect != 0x08 /* PAGE_WRITECOPY */) { + DEBUG_LOG("CreateFileMappingA: unsupported protection 0x%x\n", flProtect); + wibo::lastError = ERROR_INVALID_PARAMETER; + closeMappingIfPossible(mapping); + return nullptr; } - assert(mmapped != MAP_FAILED); - return handles::allocDataHandle({handles::TYPE_MAPPED, mmapped, (unsigned int) size}); + if (hFile == (void *) -1) { + mapping->anonymous = true; + mapping->fd = -1; + if (size == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + closeMappingIfPossible(mapping); + return nullptr; + } + mapping->maxSize = size; + } else { + FILE *fp = files::fpFromHandle(hFile); + if (!fp) { + wibo::lastError = ERROR_INVALID_HANDLE; + closeMappingIfPossible(mapping); + return nullptr; + } + int originalFd = fileno(fp); + if (originalFd == -1) { + setLastErrorFromErrno(); + closeMappingIfPossible(mapping); + return nullptr; + } + int dupFd = fcntl(originalFd, F_DUPFD_CLOEXEC, 0); + if (dupFd == -1) { + setLastErrorFromErrno(); + closeMappingIfPossible(mapping); + return nullptr; + } + mapping->fd = dupFd; + if (size == 0) { + int64_t fileSize = getFileSize(hFile); + if (fileSize < 0) { + closeMappingIfPossible(mapping); + return nullptr; + } + size = static_cast(fileSize); + } + mapping->maxSize = size; + } + + wibo::lastError = ERROR_SUCCESS; + return handles::allocDataHandle({handles::TYPE_MAPPED, mapping, static_cast(mapping->maxSize)}); } + void *WIN_FUNC CreateFileMappingW( + void *hFile, + void *lpFileMappingAttributes, + unsigned int flProtect, + unsigned int dwMaximumSizeHigh, + unsigned int dwMaximumSizeLow, + const uint16_t *lpName) { + std::string name = wideStringToString(lpName); + return CreateFileMappingA(hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpName ? name.c_str() : nullptr); + } + + constexpr unsigned int FILE_MAP_COPY = 0x00000001; + constexpr unsigned int FILE_MAP_WRITE = 0x00000002; + constexpr unsigned int FILE_MAP_READ = 0x00000004; + constexpr unsigned int FILE_MAP_EXECUTE = 0x00000020; + void *WIN_FUNC MapViewOfFile( void *hFileMappingObject, unsigned int dwDesiredAccess, unsigned int dwFileOffsetHigh, unsigned int dwFileOffsetLow, unsigned int dwNumberOfBytesToMap) { - DEBUG_LOG("MapViewOfFile(%p, %u, %u, %u, %u)\n", hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap); + DEBUG_LOG("MapViewOfFile(%p, 0x%x, %u, %u, %u)\n", hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap); handles::Data data = handles::dataFromHandle(hFileMappingObject, false); - assert(data.type == handles::TYPE_MAPPED); - return (void*)((unsigned int) data.ptr + dwFileOffsetLow); + if (data.type != handles::TYPE_MAPPED) { + wibo::lastError = ERROR_INVALID_HANDLE; + return nullptr; + } + auto *mapping = reinterpret_cast(data.ptr); + if (!mapping) { + wibo::lastError = ERROR_INVALID_HANDLE; + return nullptr; + } + if (mapping->closed) { + wibo::lastError = ERROR_INVALID_HANDLE; + return nullptr; + } + + uint64_t offset = ((uint64_t) dwFileOffsetHigh << 32) | dwFileOffsetLow; + if (mapping->anonymous && offset != 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + size_t maxSize = mapping->maxSize; + uint64_t length = dwNumberOfBytesToMap; + if (length == 0) { + if (maxSize == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + if (offset > maxSize) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + length = maxSize - offset; + } + if (length == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + if (maxSize && offset + length > maxSize) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + + int prot = PROT_READ; + bool wantWrite = (dwDesiredAccess & FILE_MAP_WRITE) != 0; + bool wantExecute = (dwDesiredAccess & FILE_MAP_EXECUTE) != 0; + + if (mapping->protect == 0x04 /* PAGE_READWRITE */) { + if (wantWrite) { + prot |= PROT_WRITE; + } + } else { // read-only or write copy + if (wantWrite && !(dwDesiredAccess & FILE_MAP_COPY)) { + wibo::lastError = ERROR_ACCESS_DENIED; + return nullptr; + } + } + if (wantExecute) { + prot |= PROT_EXEC; + } + + int flags = 0; + if (mapping->anonymous) { + flags |= MAP_ANONYMOUS; + } + flags |= (dwDesiredAccess & FILE_MAP_COPY) ? MAP_PRIVATE : MAP_SHARED; + + size_t pageSize = static_cast(sysconf(_SC_PAGESIZE)); + off_t alignedOffset = mapping->anonymous ? 0 : static_cast(offset & ~static_cast(pageSize - 1)); + size_t offsetDelta = static_cast(offset - alignedOffset); + size_t mapLength = static_cast(length + offsetDelta); + if (mapLength < length) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + + int mmapFd = mapping->anonymous ? -1 : mapping->fd; + void *mapBase = mmap(nullptr, mapLength, prot, flags, mmapFd, alignedOffset); + if (mapBase == MAP_FAILED) { + setLastErrorFromErrno(); + return nullptr; + } + void *viewPtr = static_cast(mapBase) + offsetDelta; + g_viewInfo[viewPtr] = ViewInfo{mapBase, mapLength, mapping}; + mapping->refCount++; + wibo::lastError = ERROR_SUCCESS; + return viewPtr; } int WIN_FUNC UnmapViewOfFile(void *lpBaseAddress) { DEBUG_LOG("UnmapViewOfFile(%p)\n", lpBaseAddress); + auto it = g_viewInfo.find(lpBaseAddress); + if (it == g_viewInfo.end()) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + ViewInfo info = it->second; + g_viewInfo.erase(it); + if (info.mapBase && info.mapLength) { + munmap(info.mapBase, info.mapLength); + } + if (info.owner && info.owner->refCount > 0) { + info.owner->refCount--; + tryReleaseMapping(info.owner); + } + wibo::lastError = ERROR_SUCCESS; return 1; } - int WIN_FUNC DeleteFileA(const char* lpFileName) { + BOOL WIN_FUNC DeleteFileA(const char* lpFileName) { + if (!lpFileName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } std::string path = files::pathFromWindows(lpFileName); DEBUG_LOG("DeleteFileA %s (%s)\n", lpFileName, path.c_str()); - unlink(path.c_str()); - return 1; + if (unlink(path.c_str()) == 0) { + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + setLastErrorFromErrno(); + return FALSE; + } + + BOOL WIN_FUNC DeleteFileW(const uint16_t *lpFileName) { + if (!lpFileName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + std::string name = wideStringToString(lpFileName); + return DeleteFileA(name.c_str()); } DWORD WIN_FUNC SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) { @@ -2159,6 +2563,132 @@ namespace kernel32 { return S_OK; } + HANDLE WIN_FUNC CreateMutexW(void *lpMutexAttributes, BOOL bInitialOwner, LPCWSTR lpName) { + std::string nameLog; + if (lpName) { + nameLog = wideStringToString(reinterpret_cast(lpName)); + } else { + nameLog = ""; + } + DEBUG_LOG("CreateMutexW(name=%s, initialOwner=%d)\n", nameLog.c_str(), bInitialOwner); + (void)lpMutexAttributes; + + std::u16string name = makeMutexName(lpName); + MutexObject *obj = nullptr; + bool alreadyExists = false; + { + std::lock_guard lock(mutexRegistryLock); + if (!name.empty()) { + auto it = namedMutexes.find(name); + if (it != namedMutexes.end()) { + obj = it->second; + obj->refCount++; + alreadyExists = true; + } + } + if (!obj) { + obj = new MutexObject(); + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&obj->mutex, &attr); + pthread_mutexattr_destroy(&attr); + obj->ownerValid = false; + obj->recursionCount = 0; + obj->name = name; + obj->refCount = 1; + if (!name.empty()) { + namedMutexes[name] = obj; + } + } + } + + if (!alreadyExists && bInitialOwner) { + pthread_mutex_lock(&obj->mutex); + obj->owner = pthread_self(); + obj->ownerValid = true; + obj->recursionCount = 1; + } + + HANDLE handle = handles::allocDataHandle({handles::TYPE_MUTEX, obj, 0}); + wibo::lastError = alreadyExists ? ERROR_ALREADY_EXISTS : ERROR_SUCCESS; + return handle; + } + + BOOL WIN_FUNC ReleaseMutex(HANDLE hMutex) { + DEBUG_LOG("ReleaseMutex(%p)\n", hMutex); + auto data = handles::dataFromHandle(hMutex, false); + if (data.type != handles::TYPE_MUTEX) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + auto *obj = reinterpret_cast(data.ptr); + pthread_t self = pthread_self(); + if (!obj->ownerValid || !pthread_equal(obj->owner, self)) { + wibo::lastError = ERROR_NOT_OWNER; + return FALSE; + } + if (obj->recursionCount > 0) { + obj->recursionCount--; + } + if (obj->recursionCount == 0) { + obj->ownerValid = false; + } + pthread_mutex_unlock(&obj->mutex); + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + BOOL WIN_FUNC GetThreadTimes(HANDLE hThread, + FILETIME *lpCreationTime, + FILETIME *lpExitTime, + FILETIME *lpKernelTime, + FILETIME *lpUserTime) { + DEBUG_LOG("GetThreadTimes(%p, %p, %p, %p, %p)\n", + hThread, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime); + + if (!lpKernelTime || !lpUserTime) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + bool isPseudoCurrentThread = hThread == (HANDLE)0x100007 || hThread == (HANDLE)0xFFFFFFFE || hThread == (HANDLE)0 || hThread == (HANDLE)0xFFFFFFFF; + if (!isPseudoCurrentThread) { + DEBUG_LOG("GetThreadTimes: unsupported handle %p\n", hThread); + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + + if (lpCreationTime) { + *lpCreationTime = defaultFiletime; + } + if (lpExitTime) { + lpExitTime->dwLowDateTime = 0; + lpExitTime->dwHighDateTime = 0; + } + + struct rusage usage; + if (getrusage(RUSAGE_THREAD, &usage) == 0) { + *lpKernelTime = fileTimeFromTimeval(usage.ru_stime); + *lpUserTime = fileTimeFromTimeval(usage.ru_utime); + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + struct timespec cpuTime; + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpuTime) == 0) { + *lpKernelTime = fileTimeFromDuration(0); + *lpUserTime = fileTimeFromTimespec(cpuTime); + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + setLastErrorFromErrno(); + *lpKernelTime = fileTimeFromDuration(0); + *lpUserTime = fileTimeFromDuration(0); + return FALSE; + } + unsigned short WIN_FUNC GetFileType(void *hFile) { DEBUG_LOG("GetFileType %p\n", hFile); return 1; // FILE_TYPE_DISK @@ -2529,13 +3059,61 @@ namespace kernel32 { return FALSE; // We're not multibyte (yet?) } + constexpr unsigned int LCMAP_LOWERCASE = 0x00000100; + constexpr unsigned int LCMAP_UPPERCASE = 0x00000200; + constexpr unsigned int LCMAP_SORTKEY = 0x00000400; + constexpr unsigned int LCMAP_BYTEREV = 0x00000800; + constexpr unsigned int LCMAP_LINGUISTIC_CASING = 0x01000000; + int WIN_FUNC LCMapStringW(int Locale, unsigned int dwMapFlags, const uint16_t* lpSrcStr, int cchSrc, uint16_t* lpDestStr, int cchDest) { - DEBUG_LOG("LCMapStringW: (locale=%i, flags=%u, src=%p, dest=%p)\n", Locale, dwMapFlags, cchSrc, cchDest); - if (cchSrc < 0) { - cchSrc = wstrlen(lpSrcStr) + 1; + DEBUG_LOG("LCMapStringW(locale=%i, flags=0x%x, src=%p, dest=%p, cchSrc=%d, cchDest=%d)\n", Locale, dwMapFlags, lpSrcStr, lpDestStr, cchSrc, cchDest); + (void) Locale; + if (!lpSrcStr || cchSrc == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; } - // DEBUG_LOG("lpSrcStr: %s\n", lpSrcStr); - return 1; // success + + bool nullTerminated = cchSrc < 0; + size_t srcLen = nullTerminated ? (wstrlen(lpSrcStr) + 1) : static_cast(cchSrc); + if (srcLen == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + if (!lpDestStr || cchDest == 0) { + // Caller is asking for the required length. + wibo::lastError = ERROR_SUCCESS; + return static_cast(srcLen); + } + if (cchDest < static_cast(srcLen)) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return 0; + } + + unsigned int casingFlags = dwMapFlags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE); + unsigned int ignoredFlags = dwMapFlags & (LCMAP_LINGUISTIC_CASING); + (void) ignoredFlags; + if (dwMapFlags & (LCMAP_SORTKEY | LCMAP_BYTEREV)) { + DEBUG_LOG("LCMapStringW: unsupported mapping flags 0x%x\n", dwMapFlags); + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + std::vector buffer(srcLen, 0); + for (size_t i = 0; i < srcLen; ++i) { + uint16_t ch = lpSrcStr[i]; + if (casingFlags == LCMAP_UPPERCASE) { + buffer[i] = static_cast(std::towupper(static_cast(ch))); + } else if (casingFlags == LCMAP_LOWERCASE) { + buffer[i] = static_cast(std::towlower(static_cast(ch))); + } else { + buffer[i] = ch; + } + } + + std::memcpy(lpDestStr, buffer.data(), srcLen * sizeof(uint16_t)); + wibo::lastError = ERROR_SUCCESS; + return static_cast(srcLen); } int WIN_FUNC LCMapStringA(int Locale, unsigned int dwMapFlags, const char* lpSrcStr, int cchSrc, char* lpDestStr, int cchDest) { @@ -2592,17 +3170,31 @@ namespace kernel32 { return len; } - unsigned int WIN_FUNC SetEnvironmentVariableA(const char *lpName, const char *lpValue) { - DEBUG_LOG("SetEnvironmentVariableA: %s=%s\n", lpName, lpValue ? lpValue : ""); - if (!lpName) { - return 0; + BOOL WIN_FUNC SetEnvironmentVariableA(const char *lpName, const char *lpValue) { + DEBUG_LOG("SetEnvironmentVariableA: %s=%s\n", lpName ? lpName : "(null)", lpValue ? lpValue : "(null)"); + if (!lpName || std::strchr(lpName, '=')) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; } + int rc = 0; if (!lpValue) { - return unsetenv(lpName); + rc = unsetenv(lpName); + if (rc != 0) { + setLastErrorFromErrno(); + return FALSE; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; } std::string hostValue = convertEnvValueToHost(lpName, lpValue); const char *valuePtr = hostValue.empty() ? lpValue : hostValue.c_str(); - return setenv(lpName, valuePtr, 1 /* OVERWRITE */); + rc = setenv(lpName, valuePtr, 1 /* overwrite */); + if (rc != 0) { + setLastErrorFromErrno(); + return FALSE; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; } DWORD WIN_FUNC GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize) { @@ -2623,6 +3215,16 @@ namespace kernel32 { return len - 1; } + BOOL WIN_FUNC SetEnvironmentVariableW(const uint16_t *lpName, const uint16_t *lpValue) { + if (!lpName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + std::string name = wideStringToString(lpName); + std::string value = lpValue ? wideStringToString(lpValue) : std::string(); + return SetEnvironmentVariableA(name.c_str(), lpValue ? value.c_str() : nullptr); + } + unsigned int WIN_FUNC QueryPerformanceCounter(unsigned long int *lpPerformanceCount) { DEBUG_LOG("QueryPerformanceCounter\n"); *lpPerformanceCount = 0; @@ -2785,6 +3387,9 @@ static void *resolveByName(const char *name) { // errhandlingapi.h if (strcmp(name, "GetLastError") == 0) return (void *) kernel32::GetLastError; if (strcmp(name, "SetLastError") == 0) return (void *) kernel32::SetLastError; + if (strcmp(name, "Wow64DisableWow64FsRedirection") == 0) return (void *) kernel32::Wow64DisableWow64FsRedirection; + if (strcmp(name, "Wow64RevertWow64FsRedirection") == 0) return (void *) kernel32::Wow64RevertWow64FsRedirection; + if (strcmp(name, "RaiseException") == 0) return (void *) kernel32::RaiseException; if (strcmp(name, "AddVectoredExceptionHandler") == 0) return (void *) kernel32::AddVectoredExceptionHandler; // processthreadsapi.h @@ -2794,6 +3399,7 @@ static void *resolveByName(const char *name) { if (strcmp(name, "GetCurrentThreadId") == 0) return (void *) kernel32::GetCurrentThreadId; if (strcmp(name, "ExitProcess") == 0) return (void *) kernel32::ExitProcess; if (strcmp(name, "GetExitCodeProcess") == 0) return (void *) kernel32::GetExitCodeProcess; + if (strcmp(name, "CreateProcessW") == 0) return (void *) kernel32::CreateProcessW; if (strcmp(name, "CreateProcessA") == 0) return (void *) kernel32::CreateProcessA; if (strcmp(name, "TlsAlloc") == 0) return (void *) kernel32::TlsAlloc; if (strcmp(name, "TlsFree") == 0) return (void *) kernel32::TlsFree; @@ -2803,6 +3409,7 @@ static void *resolveByName(const char *name) { if (strcmp(name, "GetStartupInfoW") == 0) return (void *) kernel32::GetStartupInfoW; if (strcmp(name, "SetThreadStackGuarantee") == 0) return (void *) kernel32::SetThreadStackGuarantee; if (strcmp(name, "GetCurrentThread") == 0) return (void *) kernel32::GetCurrentThread; + if (strcmp(name, "GetThreadTimes") == 0) return (void *) kernel32::GetThreadTimes; if (strcmp(name, "SetThreadDescription") == 0) return (void *) kernel32::SetThreadDescription; // winnls.h @@ -2836,6 +3443,8 @@ static void *resolveByName(const char *name) { if (strcmp(name, "ReleaseSRWLockExclusive") == 0) return (void *) kernel32::ReleaseSRWLockExclusive; if (strcmp(name, "TryAcquireSRWLockExclusive") == 0) return (void *) kernel32::TryAcquireSRWLockExclusive; if (strcmp(name, "WaitForSingleObject") == 0) return (void *) kernel32::WaitForSingleObject; + if (strcmp(name, "CreateMutexW") == 0) return (void *) kernel32::CreateMutexW; + if (strcmp(name, "ReleaseMutex") == 0) return (void *) kernel32::ReleaseMutex; // winbase.h if (strcmp(name, "GlobalAlloc") == 0) return (void *) kernel32::GlobalAlloc; @@ -2867,6 +3476,7 @@ static void *resolveByName(const char *name) { if (strcmp(name, "FreeEnvironmentStringsW") == 0) return (void *) kernel32::FreeEnvironmentStringsW; if (strcmp(name, "GetEnvironmentVariableA") == 0) return (void *) kernel32::GetEnvironmentVariableA; if (strcmp(name, "SetEnvironmentVariableA") == 0) return (void *) kernel32::SetEnvironmentVariableA; + if (strcmp(name, "SetEnvironmentVariableW") == 0) return (void *) kernel32::SetEnvironmentVariableW; if (strcmp(name, "GetEnvironmentVariableW") == 0) return (void *) kernel32::GetEnvironmentVariableW; // console api @@ -2884,6 +3494,7 @@ static void *resolveByName(const char *name) { if (strcmp(name, "GetFullPathNameA") == 0) return (void *) kernel32::GetFullPathNameA; if (strcmp(name, "GetFullPathNameW") == 0) return (void *) kernel32::GetFullPathNameW; if (strcmp(name, "GetShortPathNameA") == 0) return (void *) kernel32::GetShortPathNameA; + if (strcmp(name, "GetShortPathNameW") == 0) return (void *) kernel32::GetShortPathNameW; if (strcmp(name, "FindFirstFileA") == 0) return (void *) kernel32::FindFirstFileA; if (strcmp(name, "FindFirstFileW") == 0) return (void *) kernel32::FindFirstFileW; if (strcmp(name, "FindFirstFileExA") == 0) return (void *) kernel32::FindFirstFileExA; @@ -2896,9 +3507,11 @@ static void *resolveByName(const char *name) { if (strcmp(name, "CreateFileA") == 0) return (void *) kernel32::CreateFileA; if (strcmp(name, "CreateFileW") == 0) return (void *) kernel32::CreateFileW; if (strcmp(name, "CreateFileMappingA") == 0) return (void *) kernel32::CreateFileMappingA; + if (strcmp(name, "CreateFileMappingW") == 0) return (void *) kernel32::CreateFileMappingW; if (strcmp(name, "MapViewOfFile") == 0) return (void *) kernel32::MapViewOfFile; if (strcmp(name, "UnmapViewOfFile") == 0) return (void *) kernel32::UnmapViewOfFile; if (strcmp(name, "DeleteFileA") == 0) return (void *) kernel32::DeleteFileA; + if (strcmp(name, "DeleteFileW") == 0) return (void *) kernel32::DeleteFileW; if (strcmp(name, "SetFilePointer") == 0) return (void *) kernel32::SetFilePointer; if (strcmp(name, "SetFilePointerEx") == 0) return (void *) kernel32::SetFilePointerEx; if (strcmp(name, "SetEndOfFile") == 0) return (void *) kernel32::SetEndOfFile; diff --git a/dll/psapi.cpp b/dll/psapi.cpp new file mode 100644 index 0000000..bd7e4ff --- /dev/null +++ b/dll/psapi.cpp @@ -0,0 +1,67 @@ +#include "common.h" +#include "handles.h" + +namespace psapi { + BOOL WIN_FUNC EnumProcessModules(HANDLE hProcess, HMODULE *lphModule, DWORD cb, DWORD *lpcbNeeded) { + DEBUG_LOG("EnumProcessModules(hProcess=%p, cb=%u)\n", hProcess, cb); + + bool recognizedHandle = false; + if (hProcess == (HANDLE)0xFFFFFFFF) { + recognizedHandle = true; + } else { + auto data = handles::dataFromHandle(hProcess, false); + recognizedHandle = (data.type == handles::TYPE_PROCESS); + } + if (!recognizedHandle) { + wibo::lastError = ERROR_ACCESS_DENIED; + return FALSE; + } + + HMODULE currentModule = wibo::mainModule ? reinterpret_cast(wibo::mainModule->imageBuffer) : nullptr; + DWORD required = currentModule ? sizeof(HMODULE) : 0; + if (lpcbNeeded) { + *lpcbNeeded = required; + } + + if (required == 0) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + + if (!lphModule || cb < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return FALSE; + } + + lphModule[0] = currentModule; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } +} + +static void *resolveByName(const char *name) { + DEBUG_LOG("psapi resolveByName(%s)\n", name); + if (strcmp(name, "EnumProcessModules") == 0) return (void *) psapi::EnumProcessModules; + if (strcmp(name, "K32EnumProcessModules") == 0) return (void *) psapi::EnumProcessModules; + return nullptr; +} + +static void *resolveByOrdinal(uint16_t ordinal) { + DEBUG_LOG("psapi resolveByOrdinal(%u)\n", ordinal); + switch (ordinal) { + case 4: // EnumProcessModules + return (void *) psapi::EnumProcessModules; + default: + return nullptr; + } +} + +wibo::Module lib_psapi = { + (const char *[]){ + "psapi", + "psapi.dll", + nullptr, + }, + resolveByName, + resolveByOrdinal, +}; diff --git a/dll/rpcrt4.cpp b/dll/rpcrt4.cpp new file mode 100644 index 0000000..4a77962 --- /dev/null +++ b/dll/rpcrt4.cpp @@ -0,0 +1,295 @@ +#include "common.h" + +#include +#include +#include +#include +#include +#include + +namespace { + +using RPC_STATUS = unsigned long; +using RPC_WSTR = uint16_t *; +using RPC_BINDING_HANDLE = void *; +using RPC_AUTH_IDENTITY_HANDLE = void *; +using LONG_PTR = intptr_t; +using PMIDL_STUB_DESC = void *; +using PFORMAT_STRING = unsigned char *; +using PRPC_MESSAGE = void *; + +constexpr RPC_STATUS RPC_S_OK = 0; +constexpr RPC_STATUS RPC_S_INVALID_STRING_BINDING = 1700; +constexpr RPC_STATUS RPC_S_INVALID_BINDING = 1702; +constexpr RPC_STATUS RPC_S_SERVER_UNAVAILABLE = 1722; +constexpr RPC_STATUS RPC_S_INVALID_ARG = 87; +constexpr RPC_STATUS RPC_S_OUT_OF_MEMORY = 14; + +struct RPC_SECURITY_QOS { + unsigned long Version = 0; + unsigned long Capabilities = 0; + unsigned long IdentityTracking = 0; + unsigned long ImpersonationType = 0; + void *AdditionalSecurityInfo = nullptr; +}; + +struct BindingComponents { + std::u16string objectUuid; + std::u16string protocolSequence; + std::u16string networkAddress; + std::u16string endpoint; + std::u16string options; +}; + +struct BindingHandleData { + BindingComponents components; + std::u16string bindingString; + std::u16string serverPrincipalName; + unsigned long authnLevel = 0; + unsigned long authnService = 0; + RPC_AUTH_IDENTITY_HANDLE authIdentity = nullptr; + unsigned long authzService = 0; + bool hasAuthInfo = false; + bool hasSecurityQos = false; + RPC_SECURITY_QOS securityQos = {}; + bool serverReachable = false; +}; + +union CLIENT_CALL_RETURN { + void *Pointer; + LONG_PTR Simple; +}; + +std::unordered_map g_stringBindings; +std::unordered_map> g_bindingHandles; + +std::u16string toU16(RPC_WSTR str) { + if (!str) { + return {}; + } + auto *ptr = reinterpret_cast(str); + size_t length = 0; + while (ptr[length] != 0) { + ++length; + } + return std::u16string(ptr, ptr + length); +} + +std::string narrow(const std::u16string &value) { + std::string out; + out.reserve(value.size()); + for (char16_t ch : value) { + if (ch <= 0x7F) { + out.push_back(static_cast(ch)); + } else { + out.push_back('?'); + } + } + return out; +} + +std::u16string composeString(const BindingComponents &components) { + std::u16string result; + if (!components.objectUuid.empty()) { + result += components.objectUuid; + result += u"@"; + } + if (!components.protocolSequence.empty()) { + result += components.protocolSequence; + } + if (!components.networkAddress.empty()) { + if (!components.protocolSequence.empty()) { + result += u":"; + } + result += components.networkAddress; + } + if (!components.endpoint.empty()) { + result += u"["; + result += components.endpoint; + result += u"]"; + } + if (!components.options.empty()) { + result += u"{"; + result += components.options; + result += u"}"; + } + return result; +} + +BindingHandleData *getBinding(RPC_BINDING_HANDLE handle) { + auto it = g_bindingHandles.find(handle); + if (it == g_bindingHandles.end()) { + return nullptr; + } + return it->second.get(); +} + +} // namespace + +extern "C" { + +RPC_STATUS WIN_FUNC RpcStringBindingComposeW( + RPC_WSTR objUuid, + RPC_WSTR protSeq, + RPC_WSTR networkAddr, + RPC_WSTR endpoint, + RPC_WSTR options, + RPC_WSTR *stringBinding +) { + BindingComponents components; + components.objectUuid = toU16(objUuid); + components.protocolSequence = toU16(protSeq); + components.networkAddress = toU16(networkAddr); + components.endpoint = toU16(endpoint); + components.options = toU16(options); + + std::u16string encoded = composeString(components); + DEBUG_LOG("RpcStringBindingComposeW -> %s\n", narrow(encoded).c_str()); + + if (stringBinding) { + size_t length = encoded.size(); + auto *buffer = static_cast(std::malloc((length + 1) * sizeof(char16_t))); + if (!buffer) { + return RPC_S_OUT_OF_MEMORY; + } + if (length > 0) { + std::memcpy(buffer, encoded.data(), length * sizeof(char16_t)); + } + buffer[length] = 0; + RPC_WSTR result = reinterpret_cast(buffer); + g_stringBindings[result] = components; + *stringBinding = result; + } + + return RPC_S_OK; +} + +RPC_STATUS WIN_FUNC RpcBindingFromStringBindingW(RPC_WSTR stringBinding, RPC_BINDING_HANDLE *binding) { + if (!binding) { + return RPC_S_INVALID_ARG; + } + *binding = nullptr; + if (!stringBinding) { + return RPC_S_INVALID_STRING_BINDING; + } + auto it = g_stringBindings.find(stringBinding); + if (it == g_stringBindings.end()) { + return RPC_S_INVALID_STRING_BINDING; + } + auto handleData = std::make_unique(); + handleData->components = it->second; + handleData->bindingString = composeString(handleData->components); + handleData->serverReachable = false; + RPC_BINDING_HANDLE handle = reinterpret_cast(handleData.get()); + g_bindingHandles.emplace(handle, std::move(handleData)); + *binding = handle; + DEBUG_LOG("RpcBindingFromStringBindingW(handle=%p)\n", handle); + return RPC_S_OK; +} + +RPC_STATUS WIN_FUNC RpcBindingSetAuthInfoExW( + RPC_BINDING_HANDLE binding, + RPC_WSTR serverPrincName, + unsigned long authnLevel, + unsigned long authnSvc, + RPC_AUTH_IDENTITY_HANDLE authIdentity, + unsigned long authzSvc, + RPC_SECURITY_QOS *securityQos +) { + BindingHandleData *data = getBinding(binding); + if (!data) { + return RPC_S_INVALID_BINDING; + } + data->serverPrincipalName = toU16(serverPrincName); + data->authnLevel = authnLevel; + data->authnService = authnSvc; + data->authIdentity = authIdentity; + data->authzService = authzSvc; + data->hasAuthInfo = true; + if (securityQos) { + data->securityQos = *securityQos; + data->hasSecurityQos = true; + } else { + data->hasSecurityQos = false; + } + DEBUG_LOG("RpcBindingSetAuthInfoExW(handle=%p, authnSvc=%lu, authnLevel=%lu)\n", binding, authnSvc, authnLevel); + return RPC_S_OK; +} + +RPC_STATUS WIN_FUNC RpcBindingFree(RPC_BINDING_HANDLE *binding) { + if (!binding) { + return RPC_S_INVALID_ARG; + } + RPC_BINDING_HANDLE handle = *binding; + if (!handle) { + return RPC_S_INVALID_BINDING; + } + auto it = g_bindingHandles.find(handle); + if (it == g_bindingHandles.end()) { + return RPC_S_INVALID_BINDING; + } + g_bindingHandles.erase(it); + *binding = nullptr; + DEBUG_LOG("RpcBindingFree\n"); + return RPC_S_OK; +} + +RPC_STATUS WIN_FUNC RpcStringFreeW(RPC_WSTR *string) { + if (!string) { + return RPC_S_INVALID_ARG; + } + RPC_WSTR value = *string; + if (!value) { + return RPC_S_OK; + } + auto it = g_stringBindings.find(value); + if (it != g_stringBindings.end()) { + g_stringBindings.erase(it); + } + std::free(reinterpret_cast(value)); + *string = nullptr; + return RPC_S_OK; +} + +CLIENT_CALL_RETURN __attribute__((force_align_arg_pointer, callee_pop_aggregate_return(0), cdecl)) +NdrClientCall2(PMIDL_STUB_DESC stubDescriptor, PFORMAT_STRING format, ...) { + DEBUG_LOG("STUB: NdrClientCall2 stubDescriptor=%p format=%p\n", stubDescriptor, format); + CLIENT_CALL_RETURN result = {}; + result.Simple = RPC_S_SERVER_UNAVAILABLE; + DEBUG_LOG("NdrClientCall2 returning RPC_S_SERVER_UNAVAILABLE\n"); + return result; +} + +void WIN_FUNC NdrServerCall2(PRPC_MESSAGE message) { + DEBUG_LOG("STUB: NdrServerCall2 message=%p\n", message); +} + +} // extern "C" + +namespace { + +void *resolveByName(const char *name) { + if (std::strcmp(name, "RpcStringBindingComposeW") == 0) + return (void *) RpcStringBindingComposeW; + if (std::strcmp(name, "RpcBindingFromStringBindingW") == 0) + return (void *) RpcBindingFromStringBindingW; + if (std::strcmp(name, "RpcStringFreeW") == 0) + return (void *) RpcStringFreeW; + if (std::strcmp(name, "RpcBindingFree") == 0) + return (void *) RpcBindingFree; + if (std::strcmp(name, "RpcBindingSetAuthInfoExW") == 0) + return (void *) RpcBindingSetAuthInfoExW; + if (std::strcmp(name, "NdrClientCall2") == 0) + return (void *) NdrClientCall2; + if (std::strcmp(name, "NdrServerCall2") == 0) + return (void *) NdrServerCall2; + return nullptr; +} + +} // namespace + +wibo::Module lib_rpcrt4 = { + (const char *[]){"rpcrt4", "rpcrt4.dll", nullptr}, + resolveByName, + nullptr, +}; diff --git a/handles.h b/handles.h index af96c3f..cfe0f38 100644 --- a/handles.h +++ b/handles.h @@ -4,10 +4,12 @@ namespace handles { enum Type { - TYPE_UNUSED, + TYPE_UNUSED, TYPE_FILE, TYPE_MAPPED, - TYPE_PROCESS + TYPE_PROCESS, + TYPE_TOKEN, + TYPE_MUTEX }; struct Data { diff --git a/loader.cpp b/loader.cpp index a8e1389..fc09f61 100644 --- a/loader.cpp +++ b/loader.cpp @@ -92,6 +92,17 @@ struct PEHintNameTableEntry { char name[1]; // variable length }; +struct PEDelayImportDescriptor { + uint32_t attributes; + uint32_t name; + uint32_t moduleHandle; + uint32_t importAddressTable; + uint32_t importNameTable; + uint32_t boundImportAddressTable; + uint32_t unloadInformationTable; + uint32_t timeStamp; +}; + struct PEBaseRelocationBlock { uint32_t virtualAddress; uint32_t sizeOfBlock; @@ -279,12 +290,16 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) { // Import by ordinal uint16_t ordinal = lookup & 0xFFFF; DEBUG_LOG(" Ordinal: %d\n", ordinal); - *addressTable = reinterpret_cast(resolveFuncByOrdinal(module, ordinal)); + void *func = resolveFuncByOrdinal(module, ordinal); + DEBUG_LOG(" -> %p\n", func); + *addressTable = reinterpret_cast(func); } else { // Import by name PEHintNameTableEntry *hintName = fromRVA(lookup); - DEBUG_LOG(" Name: %s\n", hintName->name); - *addressTable = reinterpret_cast(resolveFuncByName(module, hintName->name)); + DEBUG_LOG(" Name: %s (IAT=%p)\n", hintName->name, addressTable); + void *func = resolveFuncByName(module, hintName->name); + DEBUG_LOG(" -> %p\n", func); + *addressTable = reinterpret_cast(func); } ++lookupTable; ++addressTable; @@ -292,6 +307,39 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) { ++dir; } + if (header32.delayImportDescriptor.virtualAddress) { + DEBUG_LOG("Processing delay import table at RVA %x\n", header32.delayImportDescriptor.virtualAddress); + PEDelayImportDescriptor *delay = fromRVA(header32.delayImportDescriptor.virtualAddress); + while (delay->name) { + char *dllName = fromRVA(delay->name); + DEBUG_LOG("Delay DLL Name: %s\n", dllName); + uint32_t *lookupTable = fromRVA(delay->importNameTable); + uint32_t *addressTable = fromRVA(delay->importAddressTable); + HMODULE module = loadModule(dllName); + while (*lookupTable) { + uint32_t lookup = *lookupTable; + if (lookup & 0x80000000) { + uint16_t ordinal = lookup & 0xFFFF; + DEBUG_LOG(" Ordinal: %d (IAT=%p)\n", ordinal, addressTable); + *addressTable = reinterpret_cast(resolveFuncByOrdinal(module, ordinal)); + } else { + PEHintNameTableEntry *hintName = fromRVA(lookup); + DEBUG_LOG(" Name: %s\n", hintName->name); + *addressTable = reinterpret_cast(resolveFuncByName(module, hintName->name)); + } + ++lookupTable; + ++addressTable; + } + if (delay->moduleHandle) { + HMODULE *moduleSlot = fromRVA(delay->moduleHandle); + if (moduleSlot) { + *moduleSlot = module; + } + } + ++delay; + } + } + entryPoint = header32.addressOfEntryPoint ? fromRVA(header32.addressOfEntryPoint) : nullptr; return true; diff --git a/module_registry.cpp b/module_registry.cpp index 481f9cf..9e90af3 100644 --- a/module_registry.cpp +++ b/module_registry.cpp @@ -23,6 +23,7 @@ extern const wibo::Module lib_lmgr; extern const wibo::Module lib_mscoree; extern const wibo::Module lib_msvcrt; extern const wibo::Module lib_ntdll; +extern const wibo::Module lib_rpcrt4; extern const wibo::Module lib_ole32; extern const wibo::Module lib_user32; extern const wibo::Module lib_vcruntime; @@ -405,7 +406,7 @@ void ensureInitialized() { const wibo::Module *builtins[] = { &lib_advapi32, &lib_bcrypt, &lib_crt, &lib_kernel32, &lib_lmgr, &lib_mscoree, &lib_msvcrt, - &lib_ntdll, &lib_ole32, &lib_user32, &lib_vcruntime, &lib_version, nullptr, + &lib_ntdll, &lib_ole32, &lib_rpcrt4, &lib_user32, &lib_vcruntime, &lib_version, nullptr, }; for (const wibo::Module **module = builtins; *module; ++module) {