문제

BEFORE anyone asks, there is no ill intent here. This project is merely for educational and personal use, and, at the most, is designed to be a "cheat engine" or possible future anti-cheat mechanism. There is no intention of using this in any malicious manner.

I have a solution with the following 3 projects:

  • 32-bit MFC application which allows the user to choose a process to inject
  • 32-bit Win32 DLL to inject into the target process via VirtualAlloc + WriteProcessMemory + CreateRemoteThread + LoadLibrary technique
  • 32-bit Win32 Console application to test when a local injection has occurred

In the DLL, I have created the following set of functions:

////////////////
// 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;
}


After the MFC application injects the DLL into the Console test project...

It calls RemoteInitialize() to initialize the remote class and return an address in virtual memory space to the caller, where it should then be localized into a shared instance of the CDeceived class. Here's how I am handling this:

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


...but the localized class pointer doesn't seem to point to the remote one, and instead holds several NULL pointers in it's virtual table which cause access violations if you try to execute any of them.

I should probably note that I have successfully given the MFC application the proper debugging rights via OpenThreadToken, ImpersonateSelf and SetPrivilege. Would I also have to somehow lock the class's address in memory, perhaps? Is the volatile keyword not enough, or incorrectly used here? What do I need to do to retrieve the very same pointer allocated by the DLL?

Thanks in advance! Upvotes will be given for any valid advice.

도움이 되었습니까?

해결책

Each processes has a different instance of the DLL in the address space and probably on a different address, you can't simple force an process to load an DLL and expect to use it address space from another process.

There are several ways to intercommunicate your injected DLL with the injector process:

  • Shared Memory: You can share memory between two or more process using MapViewOfFile, take note that you should take care of what pointers are stored in the class instance and that virtual members don't work as the vtable belongs to another address space.

  • RPC: You can use Win32 RPC to make calls between process and share data, personally, I think that it is too much complex.

  • Named Pipes/Winsock: My preferred ones, both simple to use and you can do (almost) anything you want.

  • Microsoft Message Queue(MSMQ): Don't know much about it, I think that could be used too.

  • Win32 Messages: You can use RegisterWindowMessage to handle Windows messages across the system and share data, only useful to sharing small values(two DWORDs)

There are other ways to perform IPC as you can see on MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365574(v=vs.85).aspx

I clearly recommend you to use Named Pipes/Winsock, if so, you can use Google Protobuf to easy share data structs between processes.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top