как читать процесспамять объединение переменных внутри структуры - PullRequest
0 голосов
/ 02 августа 2020

В основном я пытаюсь перебрать адрес функции из запущенного процесса, который является фиктивной программой, которую я создал, поэтому я делаю это

IMAGE_THUNK_DATA first_thunk = { 0 };

if(!ReadProcessMemory(handle,process_base_address + import_desc.FirstThunk,&first_thunk,sizeof(first_thunk),0))
    return false;

std::cout << "Function address :" << std::hex << "0x"  << first_thunk.u1.Function << '\n';

Теперь все, что вы видите, правильно, и я прочитал целевой процесс без проблем, но теперь, когда я пытаюсь напечатать first_thunk.u1.Function, я получаю адрес, который попадет в функцию MessageBoxA. Но мне нужен его адрес, а не адрес внутри этой функции, в любом случае, если я сделаю это auto address = process_base_address + import_desc.FirstThunk;, я получу то, что хочу, а именно адрес MessageBoxA. Так что я так запутался. Почему выполнение process_base_address + import_desc.FirstThunk работает, а использование структуры, которую я читаю, нет, что является моей большой проблемой в отношении этого вопроса, когда я пытаюсь прочитать sizeof(IMAGE_THUNK_DATA), я читаю 8 байтов, что понятно, поскольку оно находится внутри объединения, которое разделяет адресное пространство для 4 переменных внутри, но как мне получить адрес каждой переменной или получить доступ к каждому адресу var или rva, используя ReadProcessMemory, поскольку он читает только 8 байтов, я не могу этого сделать.

1 Ответ

1 голос
/ 03 августа 2020

То есть ReadProcessMemory с auto address = process_base_address + import_desc.FirstThunk работает для вас, но не работает с process_base_address + import_desc.FirstThunk напрямую, я рекомендую использовать (PBYTE) Conversion.

Согласно вашему описанию sizeof (IMAGE_THUNK_DATA ) = 8, я предполагаю, что вы используете версию x64.

typedef struct _IMAGE_THUNK_DATA64 {
    union {
        ULONGLONG ForwarderString;  // PBYTE 
        ULONGLONG Function;         // PDWORD
        ULONGLONG Ordinal;
        ULONGLONG AddressOfData;    // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;

Когда максимальное значение IMAGE_THUNK_DATA равно 1, это означает, что функция импортируется в виде серийного номера, в противном случае функция импортируется в форме имени функции, и это RVA, указывающий на структуру IMAGE_IMPORT_BY_NAME. Мы можем использовать константу IMAGE_ORDINAL_FLAG, чтобы проверить, равен ли самый старший бит 1.

Для меня работает следующий пример:

#include <Windows.h>
#include <iostream>
#include <psapi.h>
#pragma warning(disable : 4996)
using namespace std;

void main()
{
    int i;
    HMODULE hModule;
    DWORD dwOffset, cbneeded;
    SIZE_T dwRet;
    DWORD dwPid = 8752; // PID of process to be hooked
    TCHAR szModName[MAX_PATH];
    TCHAR szHookModName[MAX_PATH] = { "user32.dll" };
    TCHAR szFuncName[MAX_PATH] = { "MessageBoxA" };
    IMAGE_DOS_HEADER DOSHeader;
    IMAGE_OPTIONAL_HEADER OptionalHeader;
    IMAGE_DATA_DIRECTORY DataDirectory;
    IMAGE_IMPORT_DESCRIPTOR ImportDesc;
    IMAGE_THUNK_DATA OrigThunkData;
    IMAGE_THUNK_DATA RealThunkData;
    PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)malloc(255); //function name length;
    MEMORY_BASIC_INFORMATION mbi;
    LPVOID lpBaseAddress;

    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwPid);

    if (hProcess == NULL)
    {
        printf("Fail to open process!\n");
        return;
    }

    if (!EnumProcessModules(hProcess, &hModule, sizeof(hModule), &cbneeded))
    {
        printf("Fail to enum process modules!\n");
        return;
    }

    if (!ReadProcessMemory(hProcess, hModule, (void*)&DOSHeader, sizeof(IMAGE_DOS_HEADER), &dwRet))
    {
        printf("Fail to read memory in remote process!\n");
        return;
    }

    dwOffset = DOSHeader.e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER);

    if (!ReadProcessMemory(hProcess, (PBYTE)hModule + dwOffset, (void*)&OptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER), &dwRet))
    {
        printf("Fail to read memory in remote process!\n");
        return;
    }
    DataDirectory = OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    i = 0;
    do
    {
        dwOffset = DataDirectory.VirtualAddress + sizeof(IMAGE_IMPORT_DESCRIPTOR) * i;
        if (!ReadProcessMemory(hProcess, (PBYTE)hModule + dwOffset, (void*)&ImportDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR), &dwRet))
        {
            printf("Fail to read memory in remote process!\n");
            return;
        }
        if (!ReadProcessMemory(hProcess, (PBYTE)hModule + ImportDesc.Name, (void*)szModName, MAX_PATH, &dwRet))
        {
            printf("Fail to read memory in remote process!\n");
            return;
        }
        if (stricmp(szModName, szHookModName) == 0)
            break;
        i++;
    } while (ImportDesc.Name);

    i = 0;
    do
    {
        lpBaseAddress = (PBYTE)hModule + ImportDesc.OriginalFirstThunk + sizeof(IMAGE_THUNK_DATA) * i;
        if (!ReadProcessMemory(hProcess, lpBaseAddress, (void*)&OrigThunkData, sizeof(IMAGE_THUNK_DATA), &dwRet))
        {
            printf("Fail to read memory in remote process!\n");
            return;
        }


        if (!ReadProcessMemory(hProcess, (PBYTE)hModule + ImportDesc.FirstThunk + sizeof(IMAGE_THUNK_DATA) * i, (void*)&RealThunkData, sizeof(IMAGE_THUNK_DATA), &dwRet))
        {
            printf("Fail to read memory in remote process!\n");
            return;
        }
        std::cout << "Function address :" << std::hex << "0x" << RealThunkData.u1.Function << '\n';

        lpBaseAddress = (PBYTE)hModule + ImportDesc.FirstThunk + sizeof(IMAGE_THUNK_DATA) * i;
        if (!ReadProcessMemory(hProcess, lpBaseAddress, (void*)&RealThunkData, sizeof(IMAGE_THUNK_DATA), &dwRet))
        {
            printf("Fail to read memory in remote process!\n");
            return;
        }
        std::cout << "Function address :" << std::hex << "0x" << RealThunkData.u1.Function << '\n';
        

        if ((OrigThunkData.u1.Ordinal & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG)
        {
            if (!ReadProcessMemory(hProcess, (PBYTE)hModule + OrigThunkData.u1.AddressOfData, (void*)pImportByName, sizeof(WORD) + strlen(szFuncName) + 1, &dwRet))
            {
                if (GetLastError() == ERROR_PARTIAL_COPY)
                {
                    i++;
                    continue;
                }
                else
                {
                    printf("Fail to read memory in remote process!\n");
                    return;
                }
            }
            if (pImportByName->Name[0] == '\0')
            {
                printf("Function not located!\n");
                break;
            }
            if (strcmpi((char*)pImportByName->Name, szFuncName) == 0)
            {
                printf("Function: %s  \n ", pImportByName->Name);
                break;
            }
        }
        i++;
    } while (OrigThunkData.u1.Function);

    free(pImportByName);
}

Следует отметить, что содержимое, на которое указывает OriginalFirstThunk и FirstThunk в PE-файле точно такие же, но при загрузке файла в память появляется разница: содержимое OriginalFirstThunk не изменится, но данные в FirstThunk изменятся на адрес записи функции.

целевой процесс:

#include <windows.h>
#include <iostream>
int main()
{
    printf("child\n");
    VOID *p = (VOID*)MessageBoxA;
    MessageBoxA(NULL,"TEST","TEST",0);
    getchar();
    return 0;
}

Результат:

введите описание изображения здесь

...