Использование WinAPI для получения указателя класса?

ПРЕЖДЕ ЧЕМ кто-либо спросит, здесь нет злого умысла. Этот проект предназначен исключительно для образовательного и личного использования и, самое большее, разработан как «читерский движок» или возможный будущий античитерский механизм. Мы не намерены использовать это злонамеренно.

У меня есть решение со следующими 3 проектами:

  • 32-битное приложение MFC, которое позволяет пользователю выбирать процесс для внедрения
  • 32-разрядная DLL Win32 для внедрения в целевой процесс с помощью технологии VirtualAlloc + WriteProcessMemory + CreateRemoteThread + LoadLibrary
  • 32-разрядное консольное приложение Win32 для проверки локального внедрения

В DLL я создал следующий набор функций:

////////////////
// Deceiver.h //
////////////////
#ifdef DECEIVED_EXPORTS
#   define DECEIVED_API __declspec(dllexport)
#else
#   define DECEIVED_API __declspec(dllimport)
#endif

volatile class DECEIVED_API CDeceived
{
public:
    CDeceived(void);
    virtual HANDLE WINAPI GetRunningProcess();
    virtual DWORD WINAPI GetRunningProcessId();
    virtual HANDLE WINAPI GetRunningThread();
    virtual DWORD WINAPI GetRunningThreadId();
    virtual LPVOID WINAPI Allocate(DWORD size);
    virtual BOOL WINAPI Deallocate(LPVOID address, DWORD size);
    virtual BOOL WINAPI Read(LPVOID address, LPVOID buffer, DWORD size);
    virtual BOOL WINAPI Write(LPVOID address, LPVOID buffer, DWORD size);
    virtual BOOL WINAPI ReadEx(HANDLE hProcess, LPVOID address, LPVOID buffer, DWORD size);
    virtual BOOL WINAPI WriteEx(HANDLE hProcess, LPVOID address, LPVOID buffer, DWORD size);

    WCHAR m_signature[10];
};

extern DECEIVED_API CDeceived* deceiver;
LPVOID DECEIVED_API WINAPI RemoteInitialize();


//////////////////
// Deceiver.cpp //
//////////////////
#include "stdafx.h"
#include "Deceived.h"

DECEIVED_API CDeceived* deceiver = NULL;

CDeceived::CDeceived()
{
    memcpy(&m_signature[0], L"Deceived?\0", 10);
}

HANDLE WINAPI CDeceived::GetRunningProcess()
{
    return GetCurrentProcess();
}

DWORD WINAPI CDeceived::GetRunningProcessId()
{
    return GetCurrentProcessId();
}

HANDLE WINAPI CDeceived::GetRunningThread()
{
    return GetCurrentThread();
}

DWORD WINAPI CDeceived::GetRunningThreadId()
{
    return GetCurrentThreadId();
}

LPVOID WINAPI CDeceived::Allocate(DWORD size)
{
    return VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
}

BOOL WINAPI CDeceived::Deallocate(LPVOID address, DWORD size)
{
    return VirtualFree(address, size, MEM_RELEASE);
}

BOOL WINAPI CDeceived::Read(LPVOID address, LPVOID buffer, DWORD size)
{
    DWORD dwBytesRead = 0;
    BOOL bRet = ReadProcessMemory(GetCurrentProcess(), address, buffer, size, &dwBytesRead);
    return bRet && (dwBytesRead > 0);
}

BOOL WINAPI CDeceived::Write(LPVOID address, LPVOID buffer, DWORD size)
{
    DWORD dwBytesWritten = 0;
    BOOL bRet = WriteProcessMemory(GetCurrentProcess(), address, buffer, size, &dwBytesWritten);
    return bRet && (dwBytesWritten > 0);
}

BOOL WINAPI CDeceived::ReadEx(HANDLE hProcess, LPVOID address, LPVOID buffer, DWORD size)
{
    DWORD dwBytesRead = 0;
    BOOL bRet = ReadProcessMemory(hProcess, address, buffer, size, &dwBytesRead);
    return bRet && (dwBytesRead > 0);
}

BOOL WINAPI CDeceived::WriteEx(HANDLE hProcess, LPVOID address, LPVOID buffer, DWORD size)
{
    DWORD dwBytesWritten = 0;
    BOOL bRet = WriteProcessMemory(hProcess, address, buffer, size, &dwBytesWritten);
    return bRet && (dwBytesWritten > 0);
}

LPVOID DECEIVED_API WINAPI RemoteInitialize()
{
    #ifdef _DEBUG
        MessageBoxA(NULL, "Please attach a debugger", "Deceived::RemoteInitialize", MB_ICONINFORMATION);
    #endif

    if(deceiver != NULL) delete deceiver;
    deceiver = new CDeceived();
    LPVOID lpReturn = deceiver->Allocate(sizeof(deceiver));

    if(lpReturn) {
        deceiver->Write(lpReturn, &deceiver, sizeof(deceiver));
        return lpReturn;
    }

    return NULL;
}


После того, как приложение MFC внедряет DLL в тестовый проект консоли...

Он вызывает RemoteInitialize() для инициализации удаленного класса и возврата вызывающей стороне адреса в пространстве виртуальной памяти, где он затем должен быть локализован в общий экземпляр класса CDeceived. . Вот как я справляюсь с этим:

BOOL CDeceiverHook::Validate(LPVOID lpDeceivedAddress)
{
    CDeceived *deceiver = new CDeceived();
    BOOL bRet = deceiver->ReadEx(hProcess, lpDeceivedAddress, &m_deceived, sizeof(m_deceived));
    int cmp = _wcsicmp(m_deceived->m_signature, L"Deceived?");
    return bRet && (cmp == 0);
}


...но указатель локализованного класса, похоже, не указывает на удаленный, а вместо этого содержит несколько указателей NULL в своей виртуальной таблице, которые вызывают нарушения прав доступа, если вы попытаетесь выполнить любой из них.

Вероятно, следует отметить, что я успешно предоставил приложению MFC необходимые права на отладку с помощью OpenThreadToken, ImpersonateSelf и SetPrivilege. Возможно, мне также придется как-то заблокировать адрес класса в памяти? Ключевого слова volatile недостаточно или оно используется неправильно? Что мне нужно сделать, чтобы получить тот же самый указатель, выделенный DLL?

Заранее спасибо! Голоса будут даны за любой действительный совет.


person RectangleEquals    schedule 16.02.2013    source источник


Ответы (1)


Каждый процесс имеет другой экземпляр DLL в адресном пространстве и, возможно, по другому адресу, вы не можете просто заставить процесс загрузить DLL и ожидать, что это адресное пространство будет использоваться другим процессом.

Есть несколько способов взаимодействия вашей внедренной DLL с процессом инжектора:

  • Общая память: вы можете разделить память между двумя или более процессами, используя MapViewOfFile, обратите внимание, что вы должны позаботиться о том, какие указатели хранятся в экземпляре класса, и что виртуальные члены не работают, поскольку виртуальная таблица принадлежит другому адресному пространству.

  • RPC: Вы можете использовать Win32 RPC для выполнения вызовов между процессом и обменом данными, лично я думаю, что это слишком сложно.

  • Named Pipes/Winsock: мои любимые, они просты в использовании, и вы можете делать (почти) все, что захотите.

  • Очередь сообщений Microsoft (MSMQ): мало что знаю об этом, я думаю, что это тоже можно использовать.

  • Сообщения Win32: вы можете использовать RegisterWindowMessage для обработки сообщений Windows в системе и обмена данными, что полезно только для обмена небольшими значениями (два DWORD)

Существуют и другие способы выполнения IPC, как вы можете увидеть в MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365574(v=vs.85).aspx

Я настоятельно рекомендую вам использовать Named Pipes/Winsock, если это так, вы можете использовать Google Protobuf для простого обмена структурами данных между процессами.

person greenboxal    schedule 16.02.2013
comment
Я использовал некоторые методы IPC в прошлом. Моим идеальным предпочтением была бы возможность использовать удаленный класс так же, как и с локальным классом, без сетевых средств... т. е.: remoteclass-›myvar = remoteclass-›myfunc() например, без накладных расходов otherclass-›send(myclass-›myvar) и otherclass-›recv(remoteclass-›myvar) или какой-либо другой сделки . Если это можно сделать в чистом Winapi, это, безусловно, будет бонусом. Сейчас я рассмотрю возможность использования MapViewOfFile, но мне бы очень хотелось избежать сетевых методов, таких как чума. - person RectangleEquals; 16.02.2013
comment
Ближе всего к этому вы можете использовать Win32 RPC, я нахожу это очень раздражающим и сложным, но это просто личное. Для быстрого RPC с использованием Named Pipes/Winsock без необходимости возиться с ними напрямую вы можете посмотреть здесь: code.google.com/p/protobuf/wiki/ - person greenboxal; 16.02.2013
comment
Спасибо! Давай, давай, я мог бы также (вероятно) создать COM-объект и подключиться к нему из приложения MFC. В прошлом я что-то делал для этого, внедряя DLL, которая перехватывает виртуальные таблицы Direct3D... Но перехватчики были в самой DLL, поэтому, возможно, возникнет та же проблема. - person RectangleEquals; 16.02.2013
comment
только что нашел это, и это довольно недавно: codeproject.com /Articles/4837/Introduction-to-RPC-Part-1 - person RectangleEquals; 16.02.2013
comment
Переход с ATL COM/RPC. На самом деле я помню, как возился с этим еще в Windows 98 и VC6, и да, это было все равно, что запихивать мусор себе в глотку. Но с тех пор я прошел долгий путь, и теперь это действительно имеет для меня больше смысла. Спасибо! - person RectangleEquals; 16.02.2013