sRDI icon indicating copy to clipboard operation
sRDI copied to clipboard

.rsrc section does not behave as expected

Open CodeXTF2 opened this issue 3 years ago • 3 comments

DLLs cannot use FindResource when converted into shellcode. Same code works when not loaded using sRDI. Issue seems to be the same as https://github.com/TheWover/donut/issues/70 where the PE stomping breaks the .rsrc section. EDIT: Based on PE structure, not sure if there is a workaround other than disabling the SRDI_CLEARHEADER flag. Any ideas? EDIT 2: Even with SRDI_CLEARHEADER it doesnt seem to work.

CodeXTF2 avatar Nov 02 '21 13:11 CodeXTF2

I haven't had any issues with accessing resource sections as long as SRDI_CLEARHEADER is not passed. Otherwise the PE header validation fails and the native APIs refuse to touch it. I would expect something like this to work:

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
   HRSRC rsc = FindResource(hModule, MAKEINTRESOURCE(IDI_ICON1), RT_GROUP_ICON);
   ...
}

monoxgas avatar Nov 17 '21 22:11 monoxgas

If it helps, here is some extracted code which can parse the PE resource section manually:

static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntry(void* root, PIMAGE_RESOURCE_DIRECTORY resources, LPCTSTR key)
{
	PIMAGE_RESOURCE_DIRECTORY_ENTRY entries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resources + 1);
	PIMAGE_RESOURCE_DIRECTORY_ENTRY result = NULL;
	DWORD start;
	DWORD end;
	DWORD middle;

	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 = wcslen(key);
		searchKey = key;

		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((UINT_PTR)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;
			}
		}
	}

	return result;
}

#define RVA(type, base, rva) (type)((ULONG_PTR) base + rva)

std::string * GetResourceR(HMODULE hModule, LPCTSTR name, LPCTSTR type, WORD language)
{
	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 (language == 0) {
		language = LANGIDFROMLCID(GetThreadLocale());
	}

	if (hModule == NULL)
		return NULL;

	PIMAGE_NT_HEADERS ntHeaders = RVA(PIMAGE_NT_HEADERS, hModule, ((PIMAGE_DOS_HEADER)hModule)->e_lfanew);
	PIMAGE_DATA_DIRECTORY dataDir = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
	if (!dataDir->Size)
		return NULL;

	rootResources = RVA(PIMAGE_RESOURCE_DIRECTORY, hModule, dataDir->VirtualAddress);
	foundType = _MemorySearchResourceEntry(rootResources, rootResources, type);
	if (foundType == NULL) {
		SetLastError(ERROR_RESOURCE_TYPE_NOT_FOUND);
		return NULL;
	}

	typeResources = RVA(PIMAGE_RESOURCE_DIRECTORY, hModule, dataDir->VirtualAddress + (foundType->OffsetToData & 0x7fffffff));
	foundName = _MemorySearchResourceEntry(rootResources, typeResources, name);
	if (foundName == NULL) {
		SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND);
		return NULL;
	}

	nameResources = RVA(PIMAGE_RESOURCE_DIRECTORY, hModule, dataDir->VirtualAddress + (foundName->OffsetToData & 0x7fffffff));
	foundLanguage = _MemorySearchResourceEntry(rootResources, nameResources, (LPCTSTR)(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;
		}

		foundLanguage = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(nameResources + 1);
	}

	PIMAGE_RESOURCE_DATA_ENTRY entry = RVA(PIMAGE_RESOURCE_DATA_ENTRY, hModule, dataDir->VirtualAddress + (foundLanguage->OffsetToData & 0x7fffffff));
	if (entry == NULL || !entry->OffsetToData) {
		return NULL;
	}

	return new std::string(RVA(LPSTR, hModule, entry->OffsetToData), entry->Size);
}

monoxgas avatar Nov 17 '21 22:11 monoxgas

Hi Nick,

I had the exact same issue. Once loaded, I couldn't use FindResource() anymore.

But your workaround (https://github.com/monoxgas/sRDI/issues/24#issuecomment-972201094) worked like charms :)

Thanks!.

funoverip avatar Nov 16 '23 09:11 funoverip