VirtualProtect and kernel32.dll - attempt to access invalid address
Asked Answered
O

1

9

I'm analyzing various modules loaded by the process. Unfortunately I'm not able to create the kernel32.dll memory snapshot although the function works properly with other modules (e.g. ntddl.dll). The problem is with the following code:

/* Copy code from memory  */
if (VirtualProtect((BYTE*)virtualAddress, sizeOfCode, PAGE_EXECUTE_READWRITE, &flags) == 0) {
    std::cout << "VirtualProtect failed!" << std::endl;
    std::cout << "Virtual address: " << virtualAddress << std::endl;
    std::cout << "Size of code: " << sizeOfCode << std::endl;
    std::cout << "Error code: " << GetLastError() << std::endl;
}

The result of calling this code for kernel32.dll is:

Virtual address: 747d0000
Size of code: 6a000
Error code: 0x1e7

The error description says that:

ERROR_INVALID_ADDRESS
487 (0x1E7)
Attempt to access invalid address. 

I checked the process' memory map and kernel32.dll address is correct. What's the cause?

Odontoblast answered 30/11, 2013 at 17:30 Comment(5)
It is not proper code, you must call GetLastError() immediately. The cout calls can easily modify the value since they use winapi calls as well.Electra
The result is the same when GetLastError() is called right after VirtualProtectOdontoblast
kernel32.dll is a bit special. It would make some sense to stop you interfering with it - eg to prevent various injection attacks.Darlinedarling
The same situation is with winspool.drvOdontoblast
just to say that when I wrote a debugger using the "Debugging Functions" API I got the same issue : these debugging functions allow you to create a user-mode debugger but not a kernel-mode debugger (I could not enter nor pause the process into kernel functions). thus, if I remember well, kernerl32.dll was debuggable&editable if I ran my code in administrator mode, but not ntdll.dll and many other nt*.dll files. You can see the same fact when debugging a program with Visual C++ : it's doesn't let you enter in NT functions (even when stepping in assembler) because it is a user-mode debugger.Heteroclite
E
7

Pretty hard to verify that you got the address correct, it is unusually low. I just wrote another program to test this. It enumerates the regions in kernel32.dll and calls VirtualProtect() on them:

#include <Windows.h>
#include <assert.h>
#include <iostream>


int main()
{
    HMODULE hmod = GetModuleHandle(L"kernel32.dll");
    MEMORY_BASIC_INFORMATION info;
    // Start at PE32 header
    SIZE_T len = VirtualQuery(hmod, &info, sizeof(info));
    assert(len > 0);
    BYTE* dllBase = (BYTE*)info.AllocationBase;
    BYTE* address = dllBase;
    for (;;) {
        len = VirtualQuery(address, &info, sizeof(info));
        assert(len > 0);
        if (info.AllocationBase != dllBase) break;
        std::cout << "Address: " << std::hex << info.BaseAddress;
        std::cout << " (" << std::hex << info.RegionSize << ") ";
        std::cout << " protect = " << std::hex << info.Protect;
        DWORD oldprotect;
        if (info.Protect == 0) std::cout << ", VirtualProtect skipped" << std::endl;
        else {
            BOOL ok = VirtualProtect(info.BaseAddress, info.RegionSize, PAGE_EXECUTE_READWRITE, &oldprotect);
            std::cout << ", VirtualProtect = " << (ok ? "okay" : "Failed!") << std::endl;
        }
        address = (BYTE*)info.BaseAddress + info.RegionSize;
    }
    return 0;
}

Output of this program on my machine, running Windows 8.1 x64:

Address: 77470000 (1000)  protect = 2, VirtualProtect = okay
Address: 77471000 (f000)  protect = 0, VirtualProtect skipped
Address: 77480000 (62000)  protect = 20, VirtualProtect = okay
Address: 774E2000 (e000)  protect = 0, VirtualProtect skipped
Address: 774F0000 (7e000)  protect = 2, VirtualProtect = okay
Address: 7756E000 (2000)  protect = 0, VirtualProtect skipped
Address: 77570000 (1000)  protect = 4, VirtualProtect = okay
Address: 77571000 (f000)  protect = 0, VirtualProtect skipped
Address: 77580000 (1000)  protect = 2, VirtualProtect = okay
Address: 77581000 (f000)  protect = 0, VirtualProtect skipped
Address: 77590000 (1a000)  protect = 2, VirtualProtect = okay
Address: 775AA000 (6000)  protect = 0, VirtualProtect skipped

Running it in 64-bit mode:

Address: 00007FFC4F870000 (1000)  protect = 2, VirtualProtect = okay
Address: 00007FFC4F871000 (112000)  protect = 20, VirtualProtect = okay
Address: 00007FFC4F983000 (1000)  protect = 4, VirtualProtect = okay
Address: 00007FFC4F984000 (1000)  protect = 8, VirtualProtect = okay
Address: 00007FFC4F985000 (24000)  protect = 2, VirtualProtect = okay

Clearly you have a different Windows version so be sure to run this program on your machine to get comparable results.

The conclusion I draw is there is no fundamental reason for this kind of code to fail. And if it does on your machine then that's likely to be environmental. With a very obvious candidate to be your anti-malware software, which of course has a great stake in preventing code from messing with kernel32.dll. I'm running minimal protection on my machine.

Electra answered 3/12, 2013 at 11:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.