monoxgas / sRDI

Shellcode implementation of Reflective DLL Injection. Convert DLLs to position independent shellcode
Other
2.1k stars 456 forks source link

.rsrc section does not behave as expected #24

Open CodeXTF2 opened 2 years ago

CodeXTF2 commented 2 years ago

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.

monoxgas commented 2 years ago

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 commented 2 years ago

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);
}
funoverip commented 10 months ago

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!.