hotpatchによるDirectDraw/Inputのプロキシコード挿入、実践コード

2013年6月4日

これはこの記事を見ている方達の力量を判断するための教材的なコードです、処理としては導入部にあたります。
RCXのコア部分に使っていたものでは無く、前回の記事の手法で、あらたにコア部分から書き起こしてあります。CBTフック(ウインドウの構築、破棄などを起点にdllの処理を起動できるもの)を想定したDllMain部分のコアを極限まで単純化、かつ効率化してあります。

dll上のコードを書き換えるのでアプリケーションに依存しません。hotpatch自体はMicrosoftが想定しているコードのため、決まり事を守っていれば何重にもフックすることが可能で、他人のフックコードを更にフックすることもできます。コード上ではDirectDrawとDirectInputをフックしていますが、同様の書き方でDirect3D9~のフック、およびhotpatch機構のくみ込まれたファンクションはフックが可能です。

 

DLL_PROCESS_ATTACHを捕捉、IsAppCheck() でexe判定、その後、スレッドを起動して新しいウインドウを作成していますが、これはデバッグ出力用の簡易コンソールです。AllocConsole()でコンソールを出してもいいですが、最悪nProtectに感知されるため独自で実装します。

その後、InstallProxyFunctionでCOMオブジェクトを作成する関数をフックして終了します。
あとはフックされたコードが実行されればプロキシコードが無事挿入され、各dllのメモリ内容がnProtectにより修復されてもプロキシコードは生き続けます。

InstallProxyFunctionは前回のアプリケーションに依存しないAPIフックをコード化したものなので説明は割愛します。

ProxyDirectDrawCreateEx、ProxyDirectInputCreateAに関してはプロキシコードを挟み込む処理をしています。今回は保留にしますがCProxy_IDirectDraw7、CProxy_IDirectInput7はそれぞれIDirectDraw7、IDirectInput7(ヘッダファイルを探して定義を見てみると良いでしょう)を基底としたラッパークラスです。

ProxyDirectInputCreateAの冒頭でコードセッションの情報を取得してメモリを捜索していますが、これはRO内部コードのCMouseのインスタンスポインタを検索しています。マウスの座標が保存されているアドレスを取得し、書き換えることでFreeMouseを実現するためのものです。

じつの所、他の処理を用意して、このコードを適切に使えればFreeMouseとクラー位は容易に作れます。
今回の内容の理解次第で今後どのように展開していくかを考えるつもりです。ROに限らず何か作りたい人のためにと、

あとは基本的に体が癒えていませんので気長にお待ちください。

#include "stdafx.h"

#include "tinyconsole.h"
#include "ProxyDirectDraw.h"
#include "ProxyDirectInput.h"

template <typename T>
BOOL InstallProxyFunction(LPCTSTR dllname,LPCTSTR exportname,T ProxyFunction,T *pOriginalFunction)
{
    BOOL result = FALSE;
    CString fullpath;

    TCHAR systemdir[MAX_PATH];
    HINSTANCE hDll;
    ::GetSystemDirectory( systemdir, MAX_PATH);

    fullpath.Format(_T("%s\\%s"), systemdir, dllname);
    hDll = ::LoadLibrary(fullpath);

    if( !hDll )
        return result;

    BYTE *p = (BYTE*)::GetProcAddress( hDll, exportname);

    if( p )
    {
        if(     p[ 0] == 0x8b && p[ 1] == 0xff &&
            ( ( p[-5] == 0x90 && p[-4] == 0x90 && p[-3] == 0x90 && p[-2] == 0x90 && p[-1] == 0x90 ) || 
              ( p[-5] == 0xcc && p[-4] == 0xcc && p[-3] == 0xcc && p[-2] == 0xcc && p[-1] == 0xcc ) )  )
        {
            // find hotpatch structure.
            //
            // 9090909090 nop  x 5
            // 8bff       mov  edi,edi
            //       or
            // cccccccccc int 3 x 5
            // 8bff       mov edi,edi
            DWORD flOldProtect, flDontCare;
            if ( ::VirtualProtect(   (LPVOID)&p[-5], 7 , PAGE_READWRITE, &flOldProtect) )
            {
                p[-5] = 0xe9;              // jmp
                p[ 0] = 0xeb; p[ 1] = 0xf9;// jmp short [pc-7]

                *pOriginalFunction = (T)&p[2];
                *((DWORD*)&p[-4])  = (DWORD)ProxyFunction - (DWORD)&p[-5] -5;

                ::VirtualProtect( (LPVOID)&p[-5], 7 , flOldProtect, &flDontCare);
                result = TRUE;
            }
        }else
        if( p[-5] == 0xe9 &&
            p[ 0] == 0xeb && p[ 1] == 0xf9 )
        {
            // find hotpached function.
            // jmp **** 
            // jmp short [pc -7]
            DWORD flOldProtect, flDontCare;
            if ( ::VirtualProtect(   (LPVOID)&p[-5], 7 , PAGE_READWRITE, &flOldProtect) )
            {
                *pOriginalFunction = (T)(*((DWORD*)&p[-4]) + (DWORD)&p[-5] +5);
                *((DWORD*)&p[-4])  = (DWORD)ProxyFunction - (DWORD)&p[-5] -5;

                ::VirtualProtect( (LPVOID)&p[-5], 7 , flOldProtect, &flDontCare);
                result = TRUE;
            }
        }
    }
    ::FreeLibrary(hDll);

    return result;
}

typedef HRESULT (WINAPI *tDirectDrawCreateEx)( GUID FAR *lpGUID, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter );
typedef HRESULT (WINAPI *tDirectInputCreateA)( HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTA *ppDI, LPUNKNOWN punkOuter);

tDirectDrawCreateEx OrigDirectDrawCreateEx = NULL;
tDirectInputCreateA OrigDirectInputCreateA = NULL;

HRESULT WINAPI ProxyDirectDrawCreateEx(
    GUID FAR     *lpGuid,
    LPVOID       *lplpDD,
    REFIID        iid,
    IUnknown FAR *pUnkOuter )
{
    kDD_LOGGING( ("DirectDrawCreateEx hookfunc\n") );

    HRESULT Result = OrigDirectDrawCreateEx( lpGuid, lplpDD, iid, pUnkOuter );
    if(FAILED(Result))
        return Result;

    CProxy_IDirectDraw7 *lpcDD;
    *lplpDD = lpcDD = new CProxy_IDirectDraw7((IDirectDraw7*)*lplpDD);
    lpcDD->setThis(lpcDD);

    kDD_LOGGING( ("DirectDrawCreateEx Hook hookfunc") );

    return Result;
}

typedef struct StFindMemInfo{
    unsigned char x;
    char          flag;
}StFindMemInfo;

BOOL FindCallNear(DWORD calladdress,LPBYTE address)
{
    if( address[0] == 0xe8 ){
        DWORD im_calladdress = *(DWORD*)&address[1];
        // convert to immediate address 
        im_calladdress += (DWORD)address + 5;

        if( im_calladdress == calladdress )
            return TRUE;
    }
    return FALSE;
}

BOOL FindMemPattern(StFindMemInfo *patterndata,int nums,LPBYTE address)
{
    for(int ii = 0;ii < nums;ii ++)
    {
        if( patterndata[ii].flag && ( patterndata[ii].x != address[ii] ) )
            return FALSE;
    }
    return TRUE;
}

DWORD g_ROmouse = NULL;

HRESULT WINAPI ProxyDirectInputCreateA(
    HINSTANCE hinst,
    DWORD dwVersion,
    LPDIRECTINPUTA *ppDI,
    LPUNKNOWN punkOuter )
{
    kDD_LOGGING( ("DirectInputCreateA hookfunc instance = %08X",g_ROmouse) );

    DWORD ptr_CMouse_Init = 0;
    DWORD ptr_g_mouse;
    DWORD ptr_CMouse_Init_call;
    DWORD ptr_g_renderer;

    StFindMemInfo CMouse_Init[] =
    {
        {0xa1,1},{0x00,0},{0x00,0},{0x00,0},{0x00,0}, // mov     eax,[ g_hInstance ]
        {0x53,1},                                     // push    ebx
        {0x56,1},                                     // push    esi
        {0x33,1},{0xdb,1},                            // xor     ebx,ebx
        {0x53,1},                                     // push    ebx
        {0x8b,1},{0xf1,1},                            // mov     esi,ecx
        {0x56,1},                                     // push    esi
        {0x68,1},{0x00,1},{0x07,1},{0x00,1},{0x00,1}, // push    dword 000000700h
        {0x50,1}                                      // push    eax
    };

    StFindMemInfo winmain_init_CMouse_Init_call[] =
    {
        {0xb9,1},{0x00,0},{0x00,0},{0x00,0},{0x00,0}, // mov     ecx,g_mouse
        {0xe8,1},{0x00,0},{0x00,0},{0x00,0},{0x00,0}, // call    near CMouse__Init
        {0xa1,1},{0x00,0},{0x00,0},{0x00,0},{0x00,0}, // mov     eax, g_renderer__CRenderer
    };

    LPBYTE pRagexeBase;
    MEMORY_BASIC_INFORMATION mbi;

    pRagexeBase = (LPBYTE)::GetModuleHandle(NULL);
    pRagexeBase += 0x1000;

    if( ::VirtualQuery( pRagexeBase,&mbi,sizeof(mbi) ) )
    {
        kDD_LOGGING( ("MEMORY_BASIC_INFORMATION lpAddres:%08X",pRagexeBase) );
        kDD_LOGGING( ("mbi.AllocationBase = %08X",mbi.AllocationBase) );
        kDD_LOGGING( ("mbi.BaseAddress    = %08X",mbi.BaseAddress) );
        kDD_LOGGING( ("mbi.RegionSize     = %08X",mbi.RegionSize) );
        int searchsize;

        searchsize = sizeof( CMouse_Init ) / sizeof(StFindMemInfo);
        for( UINT ii = 0; ii < mbi.RegionSize - searchsize ; ii++ )
        {
            LPBYTE pBase = (LPBYTE)mbi.BaseAddress;
            if( FindMemPattern( CMouse_Init,searchsize,&pBase[ii] ) )
            {
                ptr_CMouse_Init = (DWORD)(pBase + ii);
                break;
            }
        }
        if( ptr_CMouse_Init )
        {
            searchsize = sizeof( winmain_init_CMouse_Init_call ) / sizeof(StFindMemInfo);
            for( int ii = mbi.RegionSize - searchsize; ii >= 0 ; ii-- )
            {
                LPBYTE pBase = (LPBYTE)mbi.BaseAddress;
                if( FindMemPattern( winmain_init_CMouse_Init_call,searchsize,&pBase[ii] ) )
                {
                    if( FindCallNear(ptr_CMouse_Init,&pBase[ii+5] ) )
                    {
                        DWORD ptr_g_mouse = *(DWORD*)&pBase[ii+1];
                        kDD_LOGGING( ("find CMouse::Init call : %08X",pBase + ii) );
                        kDD_LOGGING( ("find g_mouse = %08X",ptr_g_mouse) );
                        g_ROmouse = ptr_g_mouse;
                        break;
                    }
                }
            }
        }
    }
    //
    //
    HRESULT Result = OrigDirectInputCreateA( hinst, dwVersion, ppDI, punkOuter );

    if(FAILED(Result))
        return Result;
    if(dwVersion == 0x0700){
        *ppDI = new CProxy_IDirectInput7((IDirectInput7*)*ppDI);
    }
    kDD_LOGGING( ("DirectInputCreateA Hook hookfunc") );

    return Result;
}

BOOL IsAppCheck(void)
{
    TCHAR filename[MAX_PATH];

    ::GetModuleFileName( ::GetModuleHandle( NULL ), filename, sizeof(filename)/sizeof(TCHAR) );
    PathStripPath( filename );
    // check the module filename
    if( _tcsicmp( filename, _T("Ragexe.exe") ) == 0
     || _tcsicmp( filename, _T("2011-12-01aRagexe.exe") ) == 0
        )
    {
        return TRUE;
    }
    return FALSE;
}

extern HINSTANCE g_hDLL;
HANDLE g_hDebugConsole = NULL;

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    g_hDLL = hModule;
    switch (ul_reason_for_call)
    {
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
        break;

    case DLL_PROCESS_ATTACH:
        if( IsAppCheck() )
        {
            ::DisableThreadLibraryCalls( hModule );
            HINSTANCE hinst = GetModuleHandle(NULL);
            g_hDebugConsole = (HANDLE)_beginthreadex(NULL,0,TinyConsole,(LPVOID)(&hinst),CREATE_SUSPENDED,NULL );
            ::ResumeThread( g_hDebugConsole );

            InstallProxyFunction( _T("ddraw.dll"),  _T("DirectDrawCreateEx"), ProxyDirectDrawCreateEx, &OrigDirectDrawCreateEx );
            InstallProxyFunction( _T("dinput.dll"), _T("DirectInputCreateA"), ProxyDirectInputCreateA, &OrigDirectInputCreateA );
        }
        break;
    case DLL_PROCESS_DETACH:
        if( IsAppCheck() )
        {
            if( g_hDebugConsole )
                ::CloseHandle( g_hDebugConsole );
        }
        break;
    }
    return TRUE;
}

Windows

Posted by redchat