找到全局定义的指针指向使用堆分配的地址

时间:2013-06-29 13:02:23

标签: c++ reverse-engineering

我正在进行一些逆向工程,我有一个程序,并且在其上有一个全局定义的类指针。

#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,并从那里调用ZInput::CallMe(类ZInput在主应用程序上,我想从dll调用它),因为我有CallMe的地址我只需要g_Class的地址(g_Class在主应用程序上,在主应用程序WinMain上g_Class指针将指向一个new堆分配的对象),问题是我不能出于显而易见的原因从dll重载operator new。假设在主应用程序g_Class上使用new的唯一数据类型如何找到g_Class从我的dll指向的地址?

1 个答案:

答案 0 :(得分:1)

在目标应用程序中找不到指针没有明确的方法。需要一些练习和经验。

您的案例的一个良好开端是使用调试器在原始CallMe函数的开头打破并读取ECX以获取原始类指针。现在你可以在它上面设置一个内存断点(或者使用作弊引擎“找出访问这个地址的内容”)并从那里开始工作,最终得到一个静态地址,你可以从中获得类实例的动态地址。 / p>


如果类因多态性而使用虚拟表,则可以在其中找到函数指针(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从表中读取并被调用(这里是表中的第二个条目)。

对于此示例,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仍然必须是目标的类实例而不是你自己的。