From cf7ea70facb647c76e6a4cf7cabb2b9505bdd7a7 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 4 Oct 2025 14:58:35 -0600 Subject: [PATCH] Some Atom function impls --- dll/kernel32.cpp | 12 ++ dll/kernel32/winbase.cpp | 297 +++++++++++++++++++++++++++++++++++++++ dll/kernel32/winbase.h | 6 + src/common.h | 1 + src/handles.cpp | 2 +- 5 files changed, 317 insertions(+), 1 deletion(-) diff --git a/dll/kernel32.cpp b/dll/kernel32.cpp index 6b41033..0ac702e 100644 --- a/dll/kernel32.cpp +++ b/dll/kernel32.cpp @@ -198,6 +198,18 @@ void *resolveByName(const char *name) { return (void *)kernel32::CreatePipe; // winbase.h + if (strcmp(name, "FindAtomA") == 0) + return (void *)kernel32::FindAtomA; + if (strcmp(name, "FindAtomW") == 0) + return (void *)kernel32::FindAtomW; + if (strcmp(name, "AddAtomA") == 0) + return (void *)kernel32::AddAtomA; + if (strcmp(name, "AddAtomW") == 0) + return (void *)kernel32::AddAtomW; + if (strcmp(name, "GetAtomNameA") == 0) + return (void *)kernel32::GetAtomNameA; + if (strcmp(name, "GetAtomNameW") == 0) + return (void *)kernel32::GetAtomNameW; if (strcmp(name, "GlobalAlloc") == 0) return (void *)kernel32::GlobalAlloc; if (strcmp(name, "GlobalReAlloc") == 0) diff --git a/dll/kernel32/winbase.cpp b/dll/kernel32/winbase.cpp index d07bb78..d969d23 100644 --- a/dll/kernel32/winbase.cpp +++ b/dll/kernel32/winbase.cpp @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -29,6 +31,141 @@ constexpr UINT GMEM_MODIFY = 0x0080; constexpr UINT LMEM_MOVEABLE = 0x0002; constexpr UINT LMEM_ZEROINIT = 0x0040; +constexpr ATOM kMinIntegerAtom = 0x0001; +constexpr ATOM kMaxIntegerAtom = 0xBFFF; +constexpr ATOM kMinStringAtom = 0xC000; +constexpr ATOM kMaxStringAtom = 0xFFFF; + +struct AtomData { + uint16_t refCount = 0; + std::string original; +}; + +struct AtomTable { + std::mutex mutex; + std::unordered_map stringToAtom; + std::unordered_map atomToData; + ATOM nextStringAtom = kMinStringAtom; +}; + +AtomTable &localAtomTable() { + static AtomTable table; + return table; +} + +ATOM allocateStringAtomLocked(AtomTable &table) { + constexpr unsigned int kRange = static_cast(kMaxStringAtom - kMinStringAtom + 1); + unsigned int startOffset = 0; + if (table.nextStringAtom >= kMinStringAtom && table.nextStringAtom <= kMaxStringAtom) { + startOffset = static_cast(table.nextStringAtom - kMinStringAtom); + } + for (unsigned int i = 0; i < kRange; ++i) { + unsigned int offset = (startOffset + i) % kRange; + ATOM candidate = static_cast(kMinStringAtom + offset); + if (table.atomToData.find(candidate) == table.atomToData.end()) { + table.nextStringAtom = static_cast(candidate + 1); + if (table.nextStringAtom > kMaxStringAtom) { + table.nextStringAtom = kMinStringAtom; + } + return candidate; + } + } + return 0; +} + +bool tryHandleIntegerAtomPointer(const void *ptr, ATOM &atomOut) { + uintptr_t value = reinterpret_cast(ptr); + if ((value >> 16) != 0) { + return false; + } + ATOM maybeAtom = static_cast(value & 0xFFFFu); + if (maybeAtom < kMinIntegerAtom || maybeAtom > kMaxIntegerAtom) { + wibo::lastError = ERROR_INVALID_PARAMETER; + atomOut = 0; + return true; + } + wibo::lastError = ERROR_SUCCESS; + atomOut = maybeAtom; + return true; +} + +ATOM findAtomByNormalizedKey(const std::string &normalizedKey) { + auto &table = localAtomTable(); + std::lock_guard lk(table.mutex); + auto it = table.stringToAtom.find(normalizedKey); + if (it == table.stringToAtom.end()) { + wibo::lastError = ERROR_FILE_NOT_FOUND; + return 0; + } + wibo::lastError = ERROR_SUCCESS; + return it->second; +} + +ATOM tryParseIntegerAtomString(const std::string &value, bool &handled) { + handled = false; + if (value.empty() || value[0] != '#') { + return 0; + } + char *end = nullptr; + unsigned long parsed = std::strtoul(value.c_str() + 1, &end, 10); + if (end == value.c_str() + 1 || *end != '\0') { + return 0; + } + handled = true; + if (parsed < kMinIntegerAtom || parsed > kMaxIntegerAtom) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + wibo::lastError = ERROR_SUCCESS; + return static_cast(parsed); +} + +ATOM findAtomByString(const std::string &value) { + bool handledInteger = false; + ATOM atom = tryParseIntegerAtomString(value, handledInteger); + if (handledInteger) { + return atom; + } + std::string normalized = stringToLower(value); + return findAtomByNormalizedKey(normalized); +} + +ATOM addAtomByString(const std::string &value) { + bool handledInteger = false; + ATOM atom = tryParseIntegerAtomString(value, handledInteger); + if (handledInteger) { + return atom; + } + if (value.empty() || value.size() > 255) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string normalized = stringToLower(value); + auto &table = localAtomTable(); + std::lock_guard lk(table.mutex); + auto existing = table.stringToAtom.find(normalized); + if (existing != table.stringToAtom.end()) { + auto dataIt = table.atomToData.find(existing->second); + if (dataIt != table.atomToData.end() && dataIt->second.refCount < std::numeric_limits::max()) { + dataIt->second.refCount++; + } + wibo::lastError = ERROR_SUCCESS; + return existing->second; + } + ATOM newAtom = allocateStringAtomLocked(table); + if (newAtom == 0) { + wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; + return 0; + } + AtomData data; + data.refCount = 1; + data.original = value; + table.stringToAtom.emplace(std::move(normalized), newAtom); + table.atomToData.emplace(newAtom, std::move(data)); + wibo::lastError = ERROR_SUCCESS; + return newAtom; +} + void *doAlloc(UINT dwBytes, bool zero) { if (dwBytes == 0) { dwBytes = 1; @@ -165,6 +302,166 @@ const uint16_t kComputerNameWide[] = {u'C', u'O', u'M', u'P', u'N', u'A', u'M', namespace kernel32 { +ATOM WIN_FUNC AddAtomA(LPCSTR lpString) { + ATOM atom = 0; + if (tryHandleIntegerAtomPointer(lpString, atom)) { + DEBUG_LOG("AddAtomA(int:%u)\n", atom); + return atom; + } + DEBUG_LOG("AddAtomA(%s)\n", lpString ? lpString : ""); + if (!lpString) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + size_t len = strnlen(lpString, 256); + if (len == 0 || len >= 256) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string value(lpString, len); + ATOM result = addAtomByString(value); + DEBUG_LOG("AddAtomA -> %u (lastError=%u)\n", result, wibo::lastError); + return result; +} + +ATOM WIN_FUNC AddAtomW(LPCWSTR lpString) { + ATOM atom = 0; + if (tryHandleIntegerAtomPointer(lpString, atom)) { + DEBUG_LOG("AddAtomW(int:%u)\n", atom); + return atom; + } + if (!lpString) { + DEBUG_LOG("AddAtomW()\n"); + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + size_t len = wstrnlen(reinterpret_cast(lpString), 256); + if (len == 0 || len >= 256) { + DEBUG_LOG("AddAtomW(invalid length)\n"); + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string value = wideStringToString(reinterpret_cast(lpString), static_cast(len)); + DEBUG_LOG("AddAtomW(%s)\n", value.c_str()); + ATOM result = addAtomByString(value); + DEBUG_LOG("AddAtomW -> %u (lastError=%u)\n", result, wibo::lastError); + return result; +} + +ATOM WIN_FUNC FindAtomA(LPCSTR lpString) { + ATOM atom = 0; + if (tryHandleIntegerAtomPointer(lpString, atom)) { + DEBUG_LOG("FindAtomA(int:%u)\n", atom); + return atom; + } + DEBUG_LOG("FindAtomA(%s)\n", lpString ? lpString : ""); + if (!lpString) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + size_t len = strnlen(lpString, 256); + if (len == 0 || len >= 256) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string value(lpString, len); + ATOM result = findAtomByString(value); + DEBUG_LOG("FindAtomA -> %u (lastError=%u)\n", result, wibo::lastError); + return result; +} + +ATOM WIN_FUNC FindAtomW(LPCWSTR lpString) { + ATOM atom = 0; + if (tryHandleIntegerAtomPointer(lpString, atom)) { + DEBUG_LOG("FindAtomW(int:%u)\n", atom); + return atom; + } + if (!lpString) { + DEBUG_LOG("FindAtomW()\n"); + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + size_t len = wstrnlen(reinterpret_cast(lpString), 256); + if (len == 0 || len >= 256) { + DEBUG_LOG("FindAtomW(invalid length)\n"); + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string value = wideStringToString(reinterpret_cast(lpString), static_cast(len)); + DEBUG_LOG("FindAtomW(%s)\n", value.c_str()); + ATOM result = findAtomByString(value); + DEBUG_LOG("FindAtomW -> %u (lastError=%u)\n", result, wibo::lastError); + return result; +} + +UINT WIN_FUNC GetAtomNameA(ATOM nAtom, LPSTR lpBuffer, int nSize) { + DEBUG_LOG("GetAtomNameA(%u, %p, %d)\n", nAtom, lpBuffer, nSize); + if (!lpBuffer || nSize <= 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string value; + if (nAtom >= kMinIntegerAtom && nAtom <= kMaxIntegerAtom) { + value = '#'; + value += std::to_string(nAtom); + } else { + auto &table = localAtomTable(); + std::lock_guard lk(table.mutex); + auto it = table.atomToData.find(nAtom); + if (it == table.atomToData.end()) { + wibo::lastError = ERROR_INVALID_HANDLE; + return 0; + } + value = it->second.original; + } + if (value.size() + 1 > static_cast(nSize)) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return 0; + } + std::memcpy(lpBuffer, value.c_str(), value.size()); + lpBuffer[value.size()] = '\0'; + wibo::lastError = ERROR_SUCCESS; + UINT written = static_cast(value.size()); + DEBUG_LOG("GetAtomNameA -> %u (lastError=%u)\n", written, wibo::lastError); + return written; +} + +UINT WIN_FUNC GetAtomNameW(ATOM nAtom, LPWSTR lpBuffer, int nSize) { + DEBUG_LOG("GetAtomNameW(%u, %p, %d)\n", nAtom, lpBuffer, nSize); + if (!lpBuffer || nSize <= 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string narrow; + if (nAtom >= kMinIntegerAtom && nAtom <= kMaxIntegerAtom) { + narrow = '#'; + narrow += std::to_string(nAtom); + } else { + auto &table = localAtomTable(); + std::lock_guard lk(table.mutex); + auto it = table.atomToData.find(nAtom); + if (it == table.atomToData.end()) { + wibo::lastError = ERROR_INVALID_HANDLE; + return 0; + } + narrow = it->second.original; + } + auto wide = stringToWideString(narrow.c_str(), narrow.size()); + size_t needed = wide.size(); + if (needed > static_cast(nSize)) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return 0; + } + std::memcpy(lpBuffer, wide.data(), needed * sizeof(uint16_t)); + if (needed > 0) { + lpBuffer[needed - 1] = 0; + } + wibo::lastError = ERROR_SUCCESS; + UINT written = static_cast(needed ? needed - 1 : 0); + DEBUG_LOG("GetAtomNameW -> %u (lastError=%u)\n", written, wibo::lastError); + return written; +} + UINT WIN_FUNC SetHandleCount(UINT uNumber) { DEBUG_LOG("SetHandleCount(%u)\n", uNumber); (void)uNumber; diff --git a/dll/kernel32/winbase.h b/dll/kernel32/winbase.h index 992f4a7..d761661 100644 --- a/dll/kernel32/winbase.h +++ b/dll/kernel32/winbase.h @@ -8,6 +8,12 @@ namespace kernel32 { BOOL WIN_FUNC IsBadReadPtr(LPCVOID lp, UINT_PTR ucb); BOOL WIN_FUNC IsBadWritePtr(LPVOID lp, UINT_PTR ucb); +ATOM WIN_FUNC FindAtomA(LPCSTR lpString); +ATOM WIN_FUNC FindAtomW(LPCWSTR lpString); +ATOM WIN_FUNC AddAtomA(LPCSTR lpString); +ATOM WIN_FUNC AddAtomW(LPCWSTR lpString); +UINT WIN_FUNC GetAtomNameA(ATOM nAtom, LPSTR lpBuffer, int nSize); +UINT WIN_FUNC GetAtomNameW(ATOM nAtom, LPWSTR lpBuffer, int nSize); UINT WIN_FUNC SetHandleCount(UINT uNumber); DWORD WIN_FUNC FormatMessageA(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize, va_list *Arguments); diff --git a/src/common.h b/src/common.h index 3f4d56b..f7edc24 100644 --- a/src/common.h +++ b/src/common.h @@ -51,6 +51,7 @@ using FARPROC = void *; using WORD = uint16_t; using LPWORD = WORD *; using LANGID = WORD; +using ATOM = uint16_t; using DWORD = uint32_t; using PDWORD = DWORD *; using LPDWORD = DWORD *; diff --git a/src/handles.cpp b/src/handles.cpp index cccb5df..1753434 100644 --- a/src/handles.cpp +++ b/src/handles.cpp @@ -68,7 +68,7 @@ HANDLE Handles::alloc(Pin<> obj, uint32_t grantedAccess, uint32_t flags) { } Pin<> Handles::get(HANDLE h, HandleMeta *metaOut) { - if (isPseudo(h)) { + if (h == nullptr || isPseudo(h)) { return {}; // pseudo-handles have no entries }