1

私は少しリバース エンジニアリングを行っています。プログラムがあり、その上にグローバルに定義されたクラス ポインターがあります。

#include <includesandsuch.h>

myclass* g_Class = NULL;

int WinMain( ... )
{
  g_Class = new myclass(0);
}

私のクラスには、dllから呼び出したいメソッドがあります。機能する関数プロトタイプを試しました__thiscallが、クラスthisは0になり、メソッドがクラスメンバーデータに書き込むため、プログラムがクラッシュします。今、私はうまくいく解決策を思いつきました。これをSSCEまたはこの場合はSLCEと考えてください。

DWORD* g_Input = 0;
void* operator new(size_t sz)
{
    cout << "mynew" << endl; //yes i'm using (using namespace std;) but please focus on the issue
    g_Input = (DWORD*)::new char [sz];
    return g_Input;
}
void *operator new [](size_t size)
{
    // if (size > MAX_SIZE) ...
    cout << "mynew" << endl;
    return malloc(size);
}
class ZInput
{
public:
    ZInput( int n );
    ~ZInput(){};
    void CallMe( int n, DWORD b );
private:
    int m_nData;
};
ZInput::ZInput( int n ){
    m_nData = n;
}
void ZInput::CallMe( int n, DWORD b ) { 
    cout << n <<" "<< b << endl;
}
void Fake_RealSpace2_Input( int n, DWORD b )
{
    __asm
    {
        mov ecx, g_Input
        mov eax, 0x012C7310 //example address of ZInput::CallMe
        push n
        push b
        call eax
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    ZInput* pInput = new ZInput(2);
    cout << pInput << endl;
    cout << g_Input << endl;

    Fake_RealSpace2_Input(4,5 );

    delete pInput;

    return 0;
}

ZInput::CallMeこのようにして、有効なZInputクラス オブジェクトを正常に呼び出すことができます。

今私の問題は、このコードを DLL に移植し、そこから呼び出すことです (クラスZInputZInput::CallMeはメイン アプリケーションにあり、dll から呼び出したい)。(CallMeのアドレスはメイン アプリケーション上にあり、メイン アプリケーション上ではg_Class ポインタはヒープに割り当てられたオブジェクトを指します)、問題は明らかな理由で dll から operator new をオーバーロードできないことです。メインアプリケーションで使用する唯一のデータ型であると仮定すると、どのようにしてdllから指すアドレスを見つけることができますか?g_Classg_ClassWinMainnewg_Classnewg_Class

4

1 に答える 1

1

ターゲット アプリケーションでポインタを見つける確実な方法はありません。ある程度の練習と経験が必要です。

あなたの場合の良いスタートは、元の CallMe 関数の開始時にデバッガーで中断し、ECX を読み取って元のクラス ポインターを取得することです。これで、メモリ ブレークポイントを設定して (またはチート エンジンの「このアドレスにアクセスするものを見つける」を使用して)、そこから作業して、最終的にクラス インスタンスの動的アドレスをいつでも取得できる静的アドレスにたどり着くことができます。


ポリモーフィズムのためにクラスが仮想テーブルを使用する場合、そこに関数ポインタ (ZInput::CallMe) を配置できます。これは、ターゲット アプリケーションで見つけることができる仮想関数呼び出しの例です。

CPU Disasm
Address   Hex dump              Command      
006A4F94    8B4E 38             MOV ECX,(class ptr)
006A4F97    8B01                MOV EAX,DWORD PTR DS:[ECX]
006A4F99    8B50 04             MOV EDX,DWORD PTR DS:[EAX+4]
006A4F9C    FFD2                CALL EDX

クラス ptr は、この呼び出しのために ECX にロードされます。仮想テーブル ptr が読み取られます (VT は最初のメンバーとして配置されます)。メンバー関数 ptr がテーブルから読み込まれ、呼び出されます (ここでは、テーブルの 2 番目のエントリ)。

この例では、ZInput の定義は次のようになります。

class ZInput
{
public:
    virtual void unknown();
    virtual void CallMe(int, DWORD);
};

メンバー関数を呼び出すには、有効なクラスで直接呼び出すことができます (独自に割り当てたものではありません!):

ZInput *inp = *(ZInput**)(0x01112233 + 0x8); // example to get pointer
inp->CallMe(4, 5);

仮想関数テーブルを使用しない場合は、関数ポインターに静的な値を使用し、アセンブリのハッキングを呼び出す必要があります。ただし、ECX は依然としてターゲットのクラス インスタンスである必要があり、独自のものではありません。

于 2013-06-29T16:00:45.853 に答える