From a5c5bc30e6d1233975950f528b82b96e2800dea0 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Wed, 21 Sep 2016 01:52:35 +0200 Subject: [PATCH 1/3] Provide ANSI and UNICODE versions of all resource functions. --- MemoryModule.c | 353 ++++++++++++++++++++++++++++++++++--------------- MemoryModule.h | 22 ++- 2 files changed, 265 insertions(+), 110 deletions(-) diff --git a/MemoryModule.c b/MemoryModule.c index cf38388..07f0e1f 100644 --- a/MemoryModule.c +++ b/MemoryModule.c @@ -27,7 +27,6 @@ #include #include #include -#include #ifdef DEBUG_OUTPUT #include #endif @@ -792,28 +791,89 @@ int MemoryCallEntryPoint(HMEMORYMODULE mod) #define DEFAULT_LANGUAGE MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) -HMEMORYRSRC MemoryFindResource(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type) +HMEMORYRSRC MemoryFindResourceA(HMEMORYMODULE module, LPCSTR name, LPCSTR type) { - return MemoryFindResourceEx(module, name, type, DEFAULT_LANGUAGE); + return MemoryFindResourceExA(module, name, type, DEFAULT_LANGUAGE); } -static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntry( +HMEMORYRSRC MemoryFindResourceW(HMEMORYMODULE module, LPCWSTR name, LPCWSTR type) +{ + return MemoryFindResourceExW(module, name, type, DEFAULT_LANGUAGE); +} + +static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntryById( + PIMAGE_RESOURCE_DIRECTORY resources, + WORD id) +{ + PIMAGE_RESOURCE_DIRECTORY_ENTRY entries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (resources + 1); + PIMAGE_RESOURCE_DIRECTORY_ENTRY result = NULL; + DWORD start = resources->NumberOfNamedEntries; + DWORD end = start + resources->NumberOfIdEntries; + + while (end > start) { + DWORD middle = (start + end) >> 1; + WORD entryName = (WORD) entries[middle].Name; + if (id < entryName) { + end = (end != middle ? middle : middle-1); + } else if (id > entryName) { + start = (start != middle ? middle : middle+1); + } else { + result = &entries[middle]; + break; + } + } + return result; +} + +static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntryByString( void *root, PIMAGE_RESOURCE_DIRECTORY resources, - LPCTSTR key) + LPCWSTR key, + size_t keylen) { PIMAGE_RESOURCE_DIRECTORY_ENTRY entries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (resources + 1); PIMAGE_RESOURCE_DIRECTORY_ENTRY result = NULL; - DWORD start; - DWORD end; - DWORD middle; + DWORD start = 0; + DWORD end = resources->NumberOfNamedEntries; + + while (end > start) { + DWORD middle = (start + end) >> 1; + PIMAGE_RESOURCE_DIR_STRING_U resourceString = (PIMAGE_RESOURCE_DIR_STRING_U) OffsetPointer(root, entries[middle].Name & 0x7FFFFFFF); + int cmp = _wcsnicmp(key, resourceString->NameString, resourceString->Length); + if (cmp == 0) { + // Handle partial match + if (keylen > (size_t) resourceString->Length) { + cmp = 1; + } else if (keylen < (size_t) resourceString->Length) { + cmp = -1; + } + } + if (cmp < 0) { + end = (middle != end ? middle : middle-1); + } else if (cmp > 0) { + start = (middle != start ? middle : middle+1); + } else { + result = &entries[middle]; + break; + } + } + + return result; +} + +static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntryA( + void *root, + PIMAGE_RESOURCE_DIRECTORY resources, + LPCSTR key) +{ + PIMAGE_RESOURCE_DIRECTORY_ENTRY result = NULL; - if (!IS_INTRESOURCE(key) && key[0] == TEXT('#')) { + if (!IS_INTRESOURCE(key) && key[0] == '#') { // special case: resource id given as string - TCHAR *endpos = NULL; - long int tmpkey = (WORD) _tcstol((TCHAR *) &key[1], &endpos, 10); - if (tmpkey <= 0xffff && lstrlen(endpos) == 0) { - key = MAKEINTRESOURCE(tmpkey); + char *endpos = NULL; + long int tmpkey = (WORD) strtol(key + 1, &endpos, 10); + if (tmpkey <= 0xffff && strlen(endpos) == 0) { + key = MAKEINTRESOURCEA(tmpkey); } } @@ -821,88 +881,117 @@ static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntry( // followed by an ordered list of id entries - we can do // a binary search to find faster... if (IS_INTRESOURCE(key)) { - WORD check = (WORD) (uintptr_t) key; - start = resources->NumberOfNamedEntries; - end = start + resources->NumberOfIdEntries; - - while (end > start) { - WORD entryName; - middle = (start + end) >> 1; - entryName = (WORD) entries[middle].Name; - if (check < entryName) { - end = (end != middle ? middle : middle-1); - } else if (check > entryName) { - start = (start != middle ? middle : middle+1); - } else { - result = &entries[middle]; - break; - } - } - } else { - LPCWSTR searchKey; - size_t searchKeyLen = _tcslen(key); -#if defined(UNICODE) - searchKey = key; -#else - // Resource names are always stored using 16bit characters, need to - // convert string we search for. + return _MemorySearchResourceEntryById(resources, (WORD) (uintptr_t) key); + } + + // Resource names are always stored using 16bit characters, need to + // convert string we search for. #define MAX_LOCAL_KEY_LENGTH 2048 - // In most cases resource names are short, so optimize for that by - // using a pre-allocated array. - wchar_t _searchKeySpace[MAX_LOCAL_KEY_LENGTH+1]; - LPWSTR _searchKey; - if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) { - size_t _searchKeySize = (searchKeyLen + 1) * sizeof(wchar_t); - _searchKey = (LPWSTR) malloc(_searchKeySize); - if (_searchKey == NULL) { - SetLastError(ERROR_OUTOFMEMORY); - return NULL; - } - } else { - _searchKey = &_searchKeySpace[0]; + size_t searchKeyLen = strlen(key); + // In most cases resource names are short, so optimize for that by + // using a pre-allocated array. + wchar_t _searchKeySpace[MAX_LOCAL_KEY_LENGTH+1]; + LPWSTR _searchKey; + if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) { + size_t _searchKeySize = (searchKeyLen + 1) * sizeof(wchar_t); + _searchKey = (LPWSTR) malloc(_searchKeySize); + if (_searchKey == NULL) { + SetLastError(ERROR_OUTOFMEMORY); + return NULL; } + } else { + _searchKey = &_searchKeySpace[0]; + } + mbstowcs(_searchKey, key, searchKeyLen); + _searchKey[searchKeyLen] = 0; - mbstowcs(_searchKey, key, searchKeyLen); - _searchKey[searchKeyLen] = 0; - searchKey = _searchKey; -#endif - start = 0; - end = resources->NumberOfNamedEntries; - while (end > start) { - int cmp; - PIMAGE_RESOURCE_DIR_STRING_U resourceString; - middle = (start + end) >> 1; - resourceString = (PIMAGE_RESOURCE_DIR_STRING_U) OffsetPointer(root, entries[middle].Name & 0x7FFFFFFF); - cmp = _wcsnicmp(searchKey, resourceString->NameString, resourceString->Length); - if (cmp == 0) { - // Handle partial match - if (searchKeyLen > resourceString->Length) { - cmp = 1; - } else if (searchKeyLen < resourceString->Length) { - cmp = -1; - } - } - if (cmp < 0) { - end = (middle != end ? middle : middle-1); - } else if (cmp > 0) { - start = (middle != start ? middle : middle+1); - } else { - result = &entries[middle]; - break; - } + result = _MemorySearchResourceEntryByString(root, resources, _searchKey, searchKeyLen); + if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) { + free(_searchKey); + } +#undef MAX_LOCAL_KEY_LENGTH + return result; +} + +static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntryW( + void *root, + PIMAGE_RESOURCE_DIRECTORY resources, + LPCWSTR key) +{ + if (!IS_INTRESOURCE(key) && key[0] == L'#') { + // special case: resource id given as string + wchar_t *endpos = NULL; + long int tmpkey = (WORD) wcstol(key + 1, &endpos, 10); + if (tmpkey <= 0xffff && wcslen(endpos) == 0) { + key = MAKEINTRESOURCEW(tmpkey); } -#if !defined(UNICODE) - if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) { - free(_searchKey); + } + + // entries are stored as ordered list of named entries, + // followed by an ordered list of id entries - we can do + // a binary search to find faster... + if (IS_INTRESOURCE(key)) { + return _MemorySearchResourceEntryById(resources, (WORD) (uintptr_t) key); + } + + return _MemorySearchResourceEntryByString(root, resources, key, wcslen(key)); +} + +HMEMORYRSRC MemoryFindResourceExA(HMEMORYMODULE module, LPCSTR name, LPCSTR type, WORD language) +{ + unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase; + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE) module, IMAGE_DIRECTORY_ENTRY_RESOURCE); + PIMAGE_RESOURCE_DIRECTORY rootResources; + PIMAGE_RESOURCE_DIRECTORY nameResources; + PIMAGE_RESOURCE_DIRECTORY typeResources; + PIMAGE_RESOURCE_DIRECTORY_ENTRY foundType; + PIMAGE_RESOURCE_DIRECTORY_ENTRY foundName; + PIMAGE_RESOURCE_DIRECTORY_ENTRY foundLanguage; + if (directory->Size == 0) { + // no resource table found + SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); + return NULL; + } + + if (language == DEFAULT_LANGUAGE) { + // use language from current thread + language = LANGIDFROMLCID(GetThreadLocale()); + } + + // resources are stored as three-level tree + // - first node is the type + // - second node is the name + // - third node is the language + rootResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress); + foundType = _MemorySearchResourceEntryA(rootResources, rootResources, type); + if (foundType == NULL) { + SetLastError(ERROR_RESOURCE_TYPE_NOT_FOUND); + return NULL; + } + + typeResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundType->OffsetToData & 0x7fffffff)); + foundName = _MemorySearchResourceEntryA(rootResources, typeResources, name); + if (foundName == NULL) { + SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND); + return NULL; + } + + nameResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundName->OffsetToData & 0x7fffffff)); + foundLanguage = _MemorySearchResourceEntryA(rootResources, nameResources, (LPCSTR) (uintptr_t) language); + if (foundLanguage == NULL) { + // requested language not found, use first available + if (nameResources->NumberOfIdEntries == 0) { + SetLastError(ERROR_RESOURCE_LANG_NOT_FOUND); + return NULL; } -#undef MAX_LOCAL_KEY_LENGTH -#endif + + foundLanguage = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (nameResources + 1); } - return result; + return (codeBase + directory->VirtualAddress + (foundLanguage->OffsetToData & 0x7fffffff)); } -HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type, WORD language) +HMEMORYRSRC MemoryFindResourceExW(HMEMORYMODULE module, LPCWSTR name, LPCWSTR type, WORD language) { unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase; PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE) module, IMAGE_DIRECTORY_ENTRY_RESOURCE); @@ -928,21 +1017,21 @@ HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE module, LPCTSTR name, LPCTSTR typ // - second node is the name // - third node is the language rootResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress); - foundType = _MemorySearchResourceEntry(rootResources, rootResources, type); + foundType = _MemorySearchResourceEntryW(rootResources, rootResources, type); if (foundType == NULL) { SetLastError(ERROR_RESOURCE_TYPE_NOT_FOUND); return NULL; } typeResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundType->OffsetToData & 0x7fffffff)); - foundName = _MemorySearchResourceEntry(rootResources, typeResources, name); + foundName = _MemorySearchResourceEntryW(rootResources, typeResources, name); if (foundName == NULL) { SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND); return NULL; } nameResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundName->OffsetToData & 0x7fffffff)); - foundLanguage = _MemorySearchResourceEntry(rootResources, nameResources, (LPCTSTR) (uintptr_t) language); + foundLanguage = _MemorySearchResourceEntryW(rootResources, nameResources, (LPWSTR) (uintptr_t) language); if (foundLanguage == NULL) { // requested language not found, use first available if (nameResources->NumberOfIdEntries == 0) { @@ -980,49 +1069,101 @@ LPVOID MemoryLoadResource(HMEMORYMODULE module, HMEMORYRSRC resource) } int -MemoryLoadString(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize) +MemoryLoadStringA(HMEMORYMODULE module, UINT id, LPSTR buffer, int maxsize) { - return MemoryLoadStringEx(module, id, buffer, maxsize, DEFAULT_LANGUAGE); + return MemoryLoadStringExA(module, id, buffer, maxsize, DEFAULT_LANGUAGE); +} + +int +MemoryLoadStringW(HMEMORYMODULE module, UINT id, LPWSTR buffer, int maxsize) +{ + return MemoryLoadStringExW(module, id, buffer, maxsize, DEFAULT_LANGUAGE); +} + +static BOOL +MemoryLoadStringData(HMEMORYMODULE module, UINT id, HMEMORYRSRC resource, PIMAGE_RESOURCE_DIR_STRING_U* res) +{ + PIMAGE_RESOURCE_DIR_STRING_U data; + + data = (PIMAGE_RESOURCE_DIR_STRING_U) MemoryLoadResource(module, resource); + id = id & 0x0f; + while (id--) { + data = (PIMAGE_RESOURCE_DIR_STRING_U) OffsetPointer(data, (data->Length + 1) * sizeof(WCHAR)); + } + if (data->Length == 0) { + SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND); + return FALSE; + } + + *res = data; + return TRUE; } int -MemoryLoadStringEx(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize, WORD language) +MemoryLoadStringExA(HMEMORYMODULE module, UINT id, LPSTR buffer, int maxsize, WORD language) { HMEMORYRSRC resource; PIMAGE_RESOURCE_DIR_STRING_U data; - DWORD size; + int size; + if (maxsize == 0) { + // TODO(fancycode): Should provide pointer to raw data in "buffer". + // Check what should be returned in this case (bytes or characters?). + buffer[0] = 0; return 0; } - resource = MemoryFindResourceEx(module, MAKEINTRESOURCE((id >> 4) + 1), RT_STRING, language); + resource = MemoryFindResourceExA(module, MAKEINTRESOURCEA((id >> 4) + 1), (LPCSTR) RT_STRING, language); if (resource == NULL) { buffer[0] = 0; return 0; } - data = (PIMAGE_RESOURCE_DIR_STRING_U) MemoryLoadResource(module, resource); - id = id & 0x0f; - while (id--) { - data = (PIMAGE_RESOURCE_DIR_STRING_U) OffsetPointer(data, (data->Length + 1) * sizeof(WCHAR)); - } - if (data->Length == 0) { - SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND); + if (!MemoryLoadStringData(module, id, resource, &data)) { buffer[0] = 0; return 0; } size = data->Length; - if (size >= (DWORD) maxsize) { + if (size >= maxsize) { size = maxsize; } else { buffer[size] = 0; } -#if defined(UNICODE) - wcsncpy(buffer, data->NameString, size); -#else wcstombs(buffer, data->NameString, size); -#endif + return size; +} + +int +MemoryLoadStringExW(HMEMORYMODULE module, UINT id, LPWSTR buffer, int maxsize, WORD language) +{ + HMEMORYRSRC resource; + PIMAGE_RESOURCE_DIR_STRING_U data; + int size; + + if (maxsize == 0) { + // TODO(fancycode): Should provide pointer to raw data in "buffer". + // Check what should be returned in this case (bytes or characters?). + buffer[0] = 0; + return 0; + } + + resource = MemoryFindResourceExW(module, MAKEINTRESOURCEW((id >> 4) + 1), (LPCWSTR) RT_STRING, language); + if (resource == NULL) { + buffer[0] = 0; + return 0; + } + + if (!MemoryLoadStringData(module, id, resource, &data)) { + buffer[0] = 0; + return 0; + } + + size = data->Length; + if (size >= maxsize) { + size = maxsize - 1; + } + wcsncpy(buffer, data->NameString, size); return size; } diff --git a/MemoryModule.h b/MemoryModule.h index a728f6b..df68957 100644 --- a/MemoryModule.h +++ b/MemoryModule.h @@ -39,6 +39,12 @@ typedef void *HCUSTOMMODULE; extern "C" { #endif +#if defined(UNICODE) +#define MEMORYMODULE_FUNCTION_AW(func) func##W +#else +#define MEMORYMODULE_FUNCTION_AW(func) func##A +#endif + typedef LPVOID (*CustomAllocFunc)(LPVOID, SIZE_T, DWORD, DWORD, void*); typedef BOOL (*CustomFreeFunc)(LPVOID, SIZE_T, DWORD, void*); typedef HCUSTOMMODULE (*CustomLoadLibraryFunc)(LPCSTR, void *); @@ -94,12 +100,16 @@ int MemoryCallEntryPoint(HMEMORYMODULE); /** * Find the location of a resource with the specified type and name. */ -HMEMORYRSRC MemoryFindResource(HMEMORYMODULE, LPCTSTR, LPCTSTR); +HMEMORYRSRC MemoryFindResourceA(HMEMORYMODULE, LPCSTR, LPCSTR); +HMEMORYRSRC MemoryFindResourceW(HMEMORYMODULE, LPCWSTR, LPCWSTR); +#define MemoryFindResource MEMORYMODULE_FUNCTION_AW(MemoryFindResource) /** * Find the location of a resource with the specified type, name and language. */ -HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE, LPCTSTR, LPCTSTR, WORD); +HMEMORYRSRC MemoryFindResourceExA(HMEMORYMODULE, LPCSTR, LPCSTR, WORD); +HMEMORYRSRC MemoryFindResourceExW(HMEMORYMODULE, LPCWSTR, LPCWSTR, WORD); +#define MemoryFindResourceEx MEMORYMODULE_FUNCTION_AW(MemoryFindResourceEx) /** * Get the size of the resource in bytes. @@ -114,12 +124,16 @@ LPVOID MemoryLoadResource(HMEMORYMODULE, HMEMORYRSRC); /** * Load a string resource. */ -int MemoryLoadString(HMEMORYMODULE, UINT, LPTSTR, int); +int MemoryLoadStringA(HMEMORYMODULE, UINT, LPSTR, int); +int MemoryLoadStringW(HMEMORYMODULE, UINT, LPWSTR, int); +#define MemoryLoadString MEMORYMODULE_FUNCTION_AW(MemoryLoadString) /** * Load a string resource with a given language. */ -int MemoryLoadStringEx(HMEMORYMODULE, UINT, LPTSTR, int, WORD); +int MemoryLoadStringExA(HMEMORYMODULE, UINT, LPSTR, int, WORD); +int MemoryLoadStringExW(HMEMORYMODULE, UINT, LPWSTR, int, WORD); +#define MemoryLoadStringEx MEMORYMODULE_FUNCTION_AW(MemoryLoadStringEx) /** * Default implementation of CustomAllocFunc that calls VirtualAlloc From 13e979e0b71ac294cb9b7aee766cad198d0fa2a5 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Wed, 21 Sep 2016 01:55:57 +0200 Subject: [PATCH 2/3] MSVC needs the variables to be declared earlier. --- MemoryModule.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/MemoryModule.c b/MemoryModule.c index 07f0e1f..8431c54 100644 --- a/MemoryModule.c +++ b/MemoryModule.c @@ -867,6 +867,12 @@ static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntryA( LPCSTR key) { PIMAGE_RESOURCE_DIRECTORY_ENTRY result = NULL; + size_t searchKeyLen; +#define MAX_LOCAL_KEY_LENGTH 2048 + // In most cases resource names are short, so optimize for that by + // using a pre-allocated array. + wchar_t _searchKeySpace[MAX_LOCAL_KEY_LENGTH+1]; + LPWSTR _searchKey; if (!IS_INTRESOURCE(key) && key[0] == '#') { // special case: resource id given as string @@ -886,12 +892,7 @@ static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntryA( // Resource names are always stored using 16bit characters, need to // convert string we search for. -#define MAX_LOCAL_KEY_LENGTH 2048 - size_t searchKeyLen = strlen(key); - // In most cases resource names are short, so optimize for that by - // using a pre-allocated array. - wchar_t _searchKeySpace[MAX_LOCAL_KEY_LENGTH+1]; - LPWSTR _searchKey; + searchKeyLen = strlen(key); if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) { size_t _searchKeySize = (searchKeyLen + 1) * sizeof(wchar_t); _searchKey = (LPWSTR) malloc(_searchKeySize); From d718478cfccdfc45ec2b6655727fce57fff3602e Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Wed, 21 Sep 2016 01:59:27 +0200 Subject: [PATCH 3/3] Fix string termination for UNICODE. --- MemoryModule.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MemoryModule.c b/MemoryModule.c index 8431c54..817ccef 100644 --- a/MemoryModule.c +++ b/MemoryModule.c @@ -1162,7 +1162,9 @@ MemoryLoadStringExW(HMEMORYMODULE module, UINT id, LPWSTR buffer, int maxsize, W size = data->Length; if (size >= maxsize) { - size = maxsize - 1; + size = maxsize; + } else { + buffer[size] = 0; } wcsncpy(buffer, data->NameString, size); return size;