Перейти к содержанию

MasterGH

Ветераны
  • Постов

    2 999
  • Зарегистрирован

  • Победитель дней

    129

Сообщения, опубликованные MasterGH

  1. Сегодня наткнулся на полезное описание функции __RTDynamicCast похожее на _RTDynamicCast. И её и другие функции я размещу ниже.

    В игре Sniper: Ghost Warrior

    из информации сохранения конструктор создаёт в объекте игрока массив (указателей 30 подобъектов и 30-ти типов) из них не более 8-ми могут быть гранаты.

    Массив типа {{DWORD,VOID *}, ...}

    post-3-1277884179,96_thumb.png

    Первый например 0x14 (это ID типа гранат)

    Второй например 16A324D8 (это указатель на объект гранаты)

    В конструкторе есть функция _RTDynamicCast привидения любого из 30 классов сохранённых объектов к игровым объектам.

    _RTDynamicCast(v21, 0, &off_10A14088, &off_10A19214, 0);

    где

    первый парметр указатель на объект, который приводим к базовому классу

    второй параметр обычно ставят ноль, не помню что это

    [off_10A14088] = "AVISaveGame";

    [off_10A19214] = "AVInvObject";

    Последний ноль - генерировать ли исключение при ошибочном привиденияя

    Описание на англ.

    * PARAMS

    * cppobj [] Any C++ object to cast

    * unknown [] Reserved, set to 0

    * src [] type_info object describing cppobj

    * dst [] type_info object describing the base class to cast to

    * do_throw [] TRUE = throw an exception if the cast fails, FALSE = don't

    Рассмотрим часть кода:

        v15 = *(_DWORD *)v4;
    v28 = v9;
    v17 = *(void (__thiscall **)(_DWORD))(v15 + 56);
    v27 = -1;
    v17(v4);
    sub_10046CD0(&v27);
    v16 = 0;
    // v3 - указатель на объект игрока
    if ( *((_DWORD *)v3 + 318) ) // если у игрока существует массив подобъектов
    {
    do
    {
    v19 = *(_DWORD *)v4;
    v27 = *((_DWORD *)v3 + 317) + 8 * v16; // вычислили позицию типа объекта
    v20 = (*(int (__thiscall **)(int))(v19 + 56))(v4);
    v21 = (dword_10AD14DC & 1) == 0;
    v18 = v27;
    *(_DWORD *)v27 = v20; // важная штука - ID типа подобъектов
    if ( v21 )
    {
    dword_10AD14DC |= 1u;
    sub_103DCD90(v18);
    atexit(sub_1069A880);
    }
    v22 = sub_103DEA70(&dword_10AD0C68);
    ++v16;
    //позиция типа v27 объекта+4
    *(_DWORD *)(v27 + 4) = _RTDynamicCast(v22, 0, &off_10A14088, &off_10A19214, 0);
    }
    while ( v16 < *((_DWORD *)v3 + 318) );// повторать пока не более 30-ти подобъектов
    }

    Полный код можно посмотреть здесь


    {
    void *v3; // ebx@1
    int v4; // esi@1
    int v5; // edi@1
    int (__thiscall *v6)(_DWORD); // eax@1
    int v7; // eax@1
    int (__thiscall *v8)(_DWORD); // edx@1
    int v9; // eax@2
    int v10; // edx@2
    void (__thiscall *v11)(_DWORD); // eax@2
    char v12; // zf@2
    int v13; // edi@4
    char v14; // cf@4
    int v15; // edx@6
    unsigned int v16; // edi@6
    void (__thiscall *v17)(_DWORD); // eax@6
    int v18; // ecx@7
    int v19; // eax@7
    int v20; // eax@7
    char v21; // zf@7
    int v22; // eax@9
    int v23; // eax@10
    char v24; // ST27_1@10
    int v26; // [sp+14h] [bp-1Ch]@3
    int v27; // [sp+18h] [bp-18h]@2
    int v28; // [sp+1Ch] [bp-14h]@2
    int v29; // [sp+20h] [bp-10h]@2
    int v30; // [sp+24h] [bp-Ch]@2
    void **v31; // [sp+28h] [bp-8h]@1
    int v32; // [sp+2Ch] [bp-4h]@1

    v3 = this;
    sub_10042F40((int)this, st7_0, a2);
    v5 = *(_DWORD *)dword_10A474FC;
    v6 = (int (__thiscall *)(_DWORD))**(_DWORD **)a2;
    v31 = &off_1081CE0C;
    v7 = v6(a2);
    v4 = (*(int (__thiscall **)(int, int))(v5 + 1820))(dword_10A474FC, v7);
    v8 = *(int (__thiscall **)(_DWORD))(*(_DWORD *)v4 + 124);
    v32 = v4;
    if ( v8(v4) == 1026 )
    {
    v10 = *(_DWORD *)v4;
    v28 = 0;
    v29 = 0;
    v30 = 0;
    v11 = *(void (__thiscall **)(_DWORD))(v10 + 56);
    v27 = 5;
    v11(v4);
    sub_1039A1D0(&v27);
    v9 = 0;
    v12 = *((_DWORD *)v3 + 321) == 0;
    v27 = 0;
    if ( !v12 )
    {
    v26 = 0;
    do
    {
    v13 = v26 + *((_DWORD *)v3 + 320);
    *(_DWORD *)v13 = (*(int (__thiscall **)(int))(*(_DWORD *)v4 + 56))(v4);
    *(_DWORD *)(v13 + 4) = (*(int (__thiscall **)(int))(*(_DWORD *)v4 + 56))(v4);
    *(_DWORD *)(v13 + 8) = (*(int (__thiscall **)(_DWORD))(*(_DWORD *)v4 + 56))(v4);
    v26 += 16;
    *(_DWORD *)(v13 + 12) = (*(int (__thiscall **)(_DWORD))(*(_DWORD *)v4 + 56))(v4);
    v14 = (unsigned int)(v27++ + 1) < *((_DWORD *)v3 + 321);
    }
    while ( v14 );
    v9 = 0;
    }
    v15 = *(_DWORD *)v4;
    v28 = v9;
    v17 = *(void (__thiscall **)(_DWORD))(v15 + 56);
    v27 = -1;
    v17(v4);
    sub_10046CD0(&v27);
    v16 = 0;
    if ( *((_DWORD *)v3 + 318) )
    {
    do
    {
    v19 = *(_DWORD *)v4;
    v27 = *((_DWORD *)v3 + 317) + 8 * v16;
    v20 = (*(int (__thiscall **)(int))(v19 + 56))(v4);
    v21 = (dword_10AD14DC & 1) == 0;
    v18 = v27;
    *(_DWORD *)v27 = v20;
    if ( v21 )
    {
    dword_10AD14DC |= 1u;
    sub_103DCD90(v18);
    atexit(sub_1069A880);
    }
    v22 = sub_103DEA70(&dword_10AD0C68);
    ++v16;
    *(_DWORD *)(v27 + 4) = _RTDynamicCast(v22, 0, &off_10A14088, &off_10A19214, 0);
    }
    while ( v16 < *((_DWORD *)v3 + 318) );
    }
    *((_DWORD *)v3 + 324) = (*(int (__thiscall **)(_DWORD))(*(_DWORD *)v4 + 56))(v4);
    v24 = (*(int (__thiscall **)(int))(*(_DWORD *)v4 + 68))(v4);
    v23 = (*(int (__thiscall **)(_DWORD))(*(_DWORD *)v4 + 56))(v4);
    *((_DWORD *)v3 + 323) = v23;
    if ( !v24 )
    {
    if ( *(_BYTE *)(dword_10A4B4DC + 264) )
    *((_DWORD *)v3 + 323) = v23 + 400;
    }
    }
    return (**(int (__thiscall ***)(_DWORD, _DWORD))v4)(v4, 1);
    }
    int __userpurge sub_10046740<eax>(void *this<ecx>, double st7_0<st0>, int a2)

    Т.е. v21 это адрес объекта с описанием AVISaveGame, который приводим AVInvObject.

    Результат: получаем привидённый объект с описанием AVInvObject

    вспомним про массив подобъектов:

    Массив типа {{DWORD,VOID *}, ...}

    Будет типа этого: {{v27,v27+4},{v27,v27+4},{v27,v27+4},{v27,v27+4}, ...}

    где v27 - тип объекта (например гранаты)

    v27+4 - указатель на объект

    Для гранат например:

    {...{0x14,адрес},{0x14,адрес},{0x14,адрес},{0x14,адрес},{0x14,адрес},{0x14,адрес},{0x14,адрес},{0x14,адрес} ...}

    там где вместо адреса 0, то граната не существует.

    Возможно сущесвует два конструктора гранат. Первый, который рассмотрели это при загрузке уровня.

    А второй конструктор работает когда берём гранату из арсенала.

    Подумаем какой конструктор нам нужен.

    При первом конструкторе нам нужен объект сохранения, а затем привидение к нему.

    При втором конструкторе скорее всего объект сохранения не нужен.

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

    Нам нужно найти код который срабатывает после броска гранаты и после него вызывать:


    for (int i; i<8; i++){
    DWORD *PTRObjGran = *((_DWORD *)v3 + 317) + 8 * i;
    if (PTRObjGran){
    PTRObjGran = new Granade();
    }
    }
    v3 = ...; // указатель на нашего героя

    Этот код найдёт гранату которая отсутствует и создаст её.

    1. Найти конструктор гранат

    2. Найти код броска гранат

    Можно ли гранату не позволить разрушить при броске. Что будет если её не разрушать. Всё кончится тем, что граната после взрыва улетела туда куда её бросили. Надо будет подойти и взять её или осуществить это программно читкодом. Данная реализация кажется трудней, чем создать гранату способом выше :) Поэтому будем придерживаться первого способа.

    Продолжение следует....

    Общая справка:

    Справка по RTDynamicCast


    type_info *src, type_info *dst,
    int do_throw)
    {
    ....
    }

    * __RTDynamicCast (MSVCRT.@)
    *
    * Dynamically cast a C++ object to one of its base classes.
    *
    * PARAMS
    * cppobj [I] Any C++ object to cast
    * unknown [I] Reserved, set to 0
    * src [I] type_info object describing cppobj
    * dst [I] type_info object describing the base class to cast to
    * do_throw [I] TRUE = throw an exception if the cast fails, FALSE = don't
    *
    * RETURNS
    * Success: The address of cppobj, cast to the object described by dst.
    * Failure: NULL, If the object to be cast has no RTTI, or dst is not a
    * valid cast for cppobj. If do_throw is TRUE, a bad_cast exception
    * is thrown and this function does not return.
    *
    * NOTES
    * This function is usually called by compiler generated code as a result
    * of using one of the C++ dynamic cast statements.
    */
    void* MSVCRT___RTDynamicCast(type_info *cppobj, int unknown,

    Справка по другим функциям:


    * msvcrt.dll C++ objects
    *
    * Copyright 2000 Jon Griffiths
    * Copyright 2003 Alexandre Julliard
    *
    * This library is free software; you can redistribute it and/or
    * modify it under the terms of the GNU Lesser General Public
    * License as published by the Free Software Foundation; either
    * version 2.1 of the License, or (at your option) any later version.
    *
    * This library is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    * Lesser General Public License for more details.
    *
    * You should have received a copy of the GNU Lesser General Public
    * License along with this library; if not, write to the Free Software
    * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    */

    #include "wine/config.h"
    #include "wine/port.h"

    #include <stdarg.h>

    #include "windef.h"
    #include "winbase.h"
    #include "winreg.h"
    #include "winternl.h"
    #include "wine/exception.h"
    #include "winnt.h"
    #include "excpt.h"
    #include "wine/debug.h"
    #include <malloc.h>
    #include <stdlib.h>

    #include <internal/wine/msvcrt.h>
    #include <internal/wine/cppexcept.h>
    #include <internal/mtdll.h>

    WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);

    /*
    * exception object: base for exception, bad_cast, bad_typeid, __non_rtti_object
    */
    typedef struct
    {
    void* pfn_vector_dtor;
    void* pfn_what;
    } exception_vtable;

    typedef struct __exception
    {
    const exception_vtable *vtable;
    char *name; /* Name of this exception, always a new copy for each object */
    int do_free; /* Whether to free 'name' in our dtor */
    } exception;

    typedef exception bad_cast;
    typedef exception bad_typeid;
    typedef exception __non_rtti_object;

    typedef struct _rtti_base_descriptor
    {
    type_info *type_descriptor;
    int num_base_classes;
    int base_class_offset;
    unsigned int flags;
    int unknown1;
    int unknown2;
    } rtti_base_descriptor;

    typedef struct _rtti_base_array
    {
    const rtti_base_descriptor *bases[3]; /* First element is the class itself */
    } rtti_base_array;

    typedef struct _rtti_object_hierachy
    {
    int unknown1;
    int unknown2;
    int array_len; /* Size of the array pointed to by 'base_classes' */
    const rtti_base_array *base_classes;
    } rtti_object_hierachy;

    typedef struct _rtti_object_locator
    {
    int unknown1;
    int base_class_offset;
    unsigned int flags;
    type_info *type_descriptor;
    const rtti_object_hierachy *type_hierachy;
    } rtti_object_locator;


    #ifdef __i386__ /* thiscall functions are i386-specific */

    #define DEFINE_THISCALL_WRAPPER0(func) \
    extern void __thiscall_ ## func(); \
    __ASM_GLOBAL_FUNC(__thiscall_ ## func, \
    "popl %eax\n\t" \
    "pushl %ecx\n\t" \
    "pushl %eax\n\t" \
    "jmp " __ASM_NAME(#func) "@4" )
    #define DEFINE_THISCALL_WRAPPER1(func) \
    extern void __thiscall_ ## func(); \
    __ASM_GLOBAL_FUNC(__thiscall_ ## func, \
    "popl %eax\n\t" \
    "pushl %ecx\n\t" \
    "pushl %eax\n\t" \
    "jmp " __ASM_NAME(#func) "@8" )

    const exception_vtable MSVCRT_exception_vtable;
    const exception_vtable MSVCRT_bad_typeid_vtable;
    const exception_vtable MSVCRT_bad_cast_vtable;
    const exception_vtable MSVCRT___non_rtti_object_vtable;
    static const exception_vtable MSVCRT_type_info_vtable;

    /* Internal common ctor for exception */
    static void WINAPI EXCEPTION_ctor(exception *_this, const char** name)
    {
    _this->vtable = &MSVCRT_exception_vtable;
    if (*name)
    {
    size_t name_len = strlen(*name) + 1;
    _this->name = MSVCRT_malloc(name_len);
    memcpy(_this->name, *name, name_len);
    _this->do_free = TRUE;
    }
    else
    {
    _this->name = NULL;
    _this->do_free = FALSE;
    }
    }

    /******************************************************************
    * ??0exception@@QAE@ABQBD@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_exception_ctor);
    exception * __stdcall MSVCRT_exception_ctor(exception * _this, const char ** name)
    {
    TRACE("(%p,%s)\n", _this, *name);
    EXCEPTION_ctor(_this, name);
    return _this;
    }

    /******************************************************************
    * ??0exception@@QAE@ABV0@@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_exception_copy_ctor);
    exception * __stdcall MSVCRT_exception_copy_ctor(exception * _this, const exception * rhs)
    {
    TRACE("(%p,%p)\n", _this, rhs);

    if (!rhs->do_free)
    {
    _this->vtable = &MSVCRT_exception_vtable;
    _this->name = rhs->name;
    _this->do_free = FALSE;
    }
    else
    EXCEPTION_ctor(_this, (const char**)&rhs->name);
    TRACE("name = %s\n", _this->name);
    return _this;
    }

    /******************************************************************
    * ??0exception@@QAE@XZ (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER0(MSVCRT_exception_default_ctor);
    exception * __stdcall MSVCRT_exception_default_ctor(exception * _this)
    {
    static const char* empty = NULL;

    TRACE("(%p)\n", _this);
    EXCEPTION_ctor(_this, ∅);
    return _this;
    }

    /******************************************************************
    * ??1exception@@UAE@XZ (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER0(MSVCRT_exception_dtor);
    void __stdcall MSVCRT_exception_dtor(exception * _this)
    {
    TRACE("(%p)\n", _this);
    _this->vtable = &MSVCRT_exception_vtable;
    if (_this->do_free) MSVCRT_free(_this->name);
    }

    /******************************************************************
    * ??4exception@@QAEAAV0@ABV0@@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_exception_opequals);
    exception * __stdcall MSVCRT_exception_opequals(exception * _this, const exception * rhs)
    {
    TRACE("(%p %p)\n", _this, rhs);
    if (_this != rhs)
    {
    MSVCRT_exception_dtor(_this);
    MSVCRT_exception_copy_ctor(_this, rhs);
    }
    TRACE("name = %s\n", _this->name);
    return _this;
    }

    /******************************************************************
    * ??_Eexception@@UAEPAXI@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_exception_vector_dtor);
    void * __stdcall MSVCRT_exception_vector_dtor(exception * _this, unsigned int flags)
    {
    TRACE("(%p %x)\n", _this, flags);
    if (flags & 2)
    {
    /* we have an array, with the number of elements stored before the first object */
    int i, *ptr = (int *)_this - 1;

    for (i = *ptr - 1; i >= 0; i--) MSVCRT_exception_dtor(_this + i);
    MSVCRT_operator_delete(ptr);
    }
    else
    {
    MSVCRT_exception_dtor(_this);
    if (flags & 1) MSVCRT_operator_delete(_this);
    }
    return _this;
    }

    /******************************************************************
    * ??_Gexception@@UAEPAXI@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_exception_scalar_dtor);
    void * __stdcall MSVCRT_exception_scalar_dtor(exception * _this, unsigned int flags)
    {
    TRACE("(%p %x)\n", _this, flags);
    MSVCRT_exception_dtor(_this);
    if (flags & 1) MSVCRT_operator_delete(_this);
    return _this;
    }

    /******************************************************************
    * ?what@exception@@UBEPBDXZ (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER0(MSVCRT_what_exception);
    const char * __stdcall MSVCRT_what_exception(exception * _this)
    {
    TRACE("(%p) returning %s\n", _this, _this->name);
    return _this->name ? _this->name : "Unknown exception";
    }

    /******************************************************************
    * ??0bad_typeid@@QAE@ABV0@@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_bad_typeid_copy_ctor);
    bad_typeid * __stdcall MSVCRT_bad_typeid_copy_ctor(bad_typeid * _this, const bad_typeid * rhs)
    {
    TRACE("(%p %p)\n", _this, rhs);
    MSVCRT_exception_copy_ctor(_this, rhs);
    _this->vtable = &MSVCRT_bad_typeid_vtable;
    return _this;
    }

    /******************************************************************
    * ??0bad_typeid@@QAE@PBD@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_bad_typeid_ctor);
    bad_typeid * __stdcall MSVCRT_bad_typeid_ctor(bad_typeid * _this, const char * name)
    {
    TRACE("(%p %s)\n", _this, name);
    EXCEPTION_ctor(_this, &name);
    _this->vtable = &MSVCRT_bad_typeid_vtable;
    return _this;
    }

    /******************************************************************
    * ??1bad_typeid@@UAE@XZ (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER0(MSVCRT_bad_typeid_dtor);
    void __stdcall MSVCRT_bad_typeid_dtor(bad_typeid * _this)
    {
    TRACE("(%p)\n", _this);
    MSVCRT_exception_dtor(_this);
    }

    /******************************************************************
    * ??4bad_typeid@@QAEAAV0@ABV0@@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_bad_typeid_opequals);
    bad_typeid * __stdcall MSVCRT_bad_typeid_opequals(bad_typeid * _this, const bad_typeid * rhs)
    {
    TRACE("(%p %p)\n", _this, rhs);
    MSVCRT_exception_opequals(_this, rhs);
    return _this;
    }

    /******************************************************************
    * ??_Ebad_typeid@@UAEPAXI@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_bad_typeid_vector_dtor);
    void * __stdcall MSVCRT_bad_typeid_vector_dtor(bad_typeid * _this, unsigned int flags)
    {
    TRACE("(%p %x)\n", _this, flags);
    if (flags & 2)
    {
    /* we have an array, with the number of elements stored before the first object */
    int i, *ptr = (int *)_this - 1;

    for (i = *ptr - 1; i >= 0; i--) MSVCRT_bad_typeid_dtor(_this + i);
    MSVCRT_operator_delete(ptr);
    }
    else
    {
    MSVCRT_bad_typeid_dtor(_this);
    if (flags & 1) MSVCRT_operator_delete(_this);
    }
    return _this;
    }

    /******************************************************************
    * ??_Gbad_typeid@@UAEPAXI@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_bad_typeid_scalar_dtor);
    void * __stdcall MSVCRT_bad_typeid_scalar_dtor(bad_typeid * _this, unsigned int flags)
    {
    TRACE("(%p %x)\n", _this, flags);
    MSVCRT_bad_typeid_dtor(_this);
    if (flags & 1) MSVCRT_operator_delete(_this);
    return _this;
    }

    /******************************************************************
    * ??0__non_rtti_object@@QAE@ABV0@@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT___non_rtti_object_copy_ctor);
    __non_rtti_object * __stdcall MSVCRT___non_rtti_object_copy_ctor(__non_rtti_object * _this,
    const __non_rtti_object * rhs)
    {
    TRACE("(%p %p)\n", _this, rhs);
    MSVCRT_bad_typeid_copy_ctor(_this, rhs);
    _this->vtable = &MSVCRT___non_rtti_object_vtable;
    return _this;
    }

    /******************************************************************
    * ??0__non_rtti_object@@QAE@PBD@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT___non_rtti_object_ctor);
    __non_rtti_object * __stdcall MSVCRT___non_rtti_object_ctor(__non_rtti_object * _this,
    const char * name)
    {
    TRACE("(%p %s)\n", _this, name);
    EXCEPTION_ctor(_this, &name);
    _this->vtable = &MSVCRT___non_rtti_object_vtable;
    return _this;
    }

    /******************************************************************
    * ??1__non_rtti_object@@UAE@XZ (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER0(MSVCRT___non_rtti_object_dtor);
    void __stdcall MSVCRT___non_rtti_object_dtor(__non_rtti_object * _this)
    {
    TRACE("(%p)\n", _this);
    MSVCRT_bad_typeid_dtor(_this);
    }

    /******************************************************************
    * ??4__non_rtti_object@@QAEAAV0@ABV0@@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT___non_rtti_object_opequals);
    __non_rtti_object * __stdcall MSVCRT___non_rtti_object_opequals(__non_rtti_object * _this,
    const __non_rtti_object *rhs)
    {
    TRACE("(%p %p)\n", _this, rhs);
    MSVCRT_bad_typeid_opequals(_this, rhs);
    return _this;
    }

    /******************************************************************
    * ??_E__non_rtti_object@@UAEPAXI@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT___non_rtti_object_vector_dtor);
    void * __stdcall MSVCRT___non_rtti_object_vector_dtor(__non_rtti_object * _this, unsigned int flags)
    {
    TRACE("(%p %x)\n", _this, flags);
    if (flags & 2)
    {
    /* we have an array, with the number of elements stored before the first object */
    int i, *ptr = (int *)_this - 1;

    for (i = *ptr - 1; i >= 0; i--) MSVCRT___non_rtti_object_dtor(_this + i);
    MSVCRT_operator_delete(ptr);
    }
    else
    {
    MSVCRT___non_rtti_object_dtor(_this);
    if (flags & 1) MSVCRT_operator_delete(_this);
    }
    return _this;
    }

    /******************************************************************
    * ??_G__non_rtti_object@@UAEPAXI@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT___non_rtti_object_scalar_dtor);
    void * __stdcall MSVCRT___non_rtti_object_scalar_dtor(__non_rtti_object * _this, unsigned int flags)
    {
    TRACE("(%p %x)\n", _this, flags);
    MSVCRT___non_rtti_object_dtor(_this);
    if (flags & 1) MSVCRT_operator_delete(_this);
    return _this;
    }

    /******************************************************************
    * ??0bad_cast@@QAE@ABQBD@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_bad_cast_ctor);
    bad_cast * __stdcall MSVCRT_bad_cast_ctor(bad_cast * _this, const char ** name)
    {
    TRACE("(%p %s)\n", _this, *name);
    EXCEPTION_ctor(_this, name);
    _this->vtable = &MSVCRT_bad_cast_vtable;
    return _this;
    }

    /******************************************************************
    * ??0bad_cast@@QAE@ABV0@@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_bad_cast_copy_ctor);
    bad_cast * __stdcall MSVCRT_bad_cast_copy_ctor(bad_cast * _this, const bad_cast * rhs)
    {
    TRACE("(%p %p)\n", _this, rhs);
    MSVCRT_exception_copy_ctor(_this, rhs);
    _this->vtable = &MSVCRT_bad_cast_vtable;
    return _this;
    }

    /******************************************************************
    * ??1bad_cast@@UAE@XZ (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER0(MSVCRT_bad_cast_dtor);
    void __stdcall MSVCRT_bad_cast_dtor(bad_cast * _this)
    {
    TRACE("(%p)\n", _this);
    MSVCRT_exception_dtor(_this);
    }

    /******************************************************************
    * ??4bad_cast@@QAEAAV0@ABV0@@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_bad_cast_opequals);
    bad_cast * __stdcall MSVCRT_bad_cast_opequals(bad_cast * _this, const bad_cast * rhs)
    {
    TRACE("(%p %p)\n", _this, rhs);
    MSVCRT_exception_opequals(_this, rhs);
    return _this;
    }

    /******************************************************************
    * ??_Ebad_cast@@UAEPAXI@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_bad_cast_vector_dtor);
    void * __stdcall MSVCRT_bad_cast_vector_dtor(bad_cast * _this, unsigned int flags)
    {
    TRACE("(%p %x)\n", _this, flags);
    if (flags & 2)
    {
    /* we have an array, with the number of elements stored before the first object */
    int i, *ptr = (int *)_this - 1;

    for (i = *ptr - 1; i >= 0; i--) MSVCRT_bad_cast_dtor(_this + i);
    MSVCRT_operator_delete(ptr);
    }
    else
    {
    MSVCRT_bad_cast_dtor(_this);
    if (flags & 1) MSVCRT_operator_delete(_this);
    }
    return _this;
    }

    /******************************************************************
    * ??_Gbad_cast@@UAEPAXI@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_bad_cast_scalar_dtor);
    void * __stdcall MSVCRT_bad_cast_scalar_dtor(bad_cast * _this, unsigned int flags)
    {
    TRACE("(%p %x)\n", _this, flags);
    MSVCRT_bad_cast_dtor(_this);
    if (flags & 1) MSVCRT_operator_delete(_this);
    return _this;
    }

    /******************************************************************
    * ??8type_info@@QBEHABV0@@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_type_info_opequals_equals);
    int __stdcall MSVCRT_type_info_opequals_equals(type_info * _this, const type_info * rhs)
    {
    int ret = !strcmp(_this->mangled + 1, rhs->mangled + 1);
    TRACE("(%p %p) returning %d\n", _this, rhs, ret);
    return ret;
    }

    /******************************************************************
    * ??9type_info@@QBEHABV0@@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_type_info_opnot_equals);
    int __stdcall MSVCRT_type_info_opnot_equals(type_info * _this, const type_info * rhs)
    {
    int ret = !!strcmp(_this->mangled + 1, rhs->mangled + 1);
    TRACE("(%p %p) returning %d\n", _this, rhs, ret);
    return ret;
    }

    /******************************************************************
    * ?before@type_info@@QBEHABV1@@Z (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_type_info_before);
    int __stdcall MSVCRT_type_info_before(type_info * _this, const type_info * rhs)
    {
    int ret = strcmp(_this->mangled + 1, rhs->mangled + 1) < 0;
    TRACE("(%p %p) returning %d\n", _this, rhs, ret);
    return ret;
    }

    /******************************************************************
    * ??1type_info@@UAE@XZ (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER0(MSVCRT_type_info_dtor);
    void __stdcall MSVCRT_type_info_dtor(type_info * _this)
    {
    TRACE("(%p)\n", _this);
    if (_this->name)
    MSVCRT_free(_this->name);
    }

    /******************************************************************
    * ?name@type_info@@QBEPBDXZ (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER0(MSVCRT_type_info_name);
    const char * __stdcall MSVCRT_type_info_name(type_info * _this)
    {
    if (!_this->name)
    {
    /* Create and set the demangled name */
    /* Nota: mangled name in type_info struct always start with a '.', while
    * it isn't valid for mangled name.
    * Is this '.' really part of the mangled name, or has it some other meaning ?
    */
    char* name = __unDName(0, _this->mangled + 1, 0,
    MSVCRT_malloc, MSVCRT_free, 0x2800);

    if (name)
    {
    unsigned int len = strlen(name);

    /* It seems _unDName may leave blanks at the end of the demangled name */
    while (len && name[--len] == ' ')
    name[len] = '\0';

    _mlock(_EXIT_LOCK2);

    if (_this->name)
    {
    /* Another thread set this member since we checked above - use it */
    MSVCRT_free(name);
    }
    else
    _this->name = name;

    _munlock(_EXIT_LOCK2);
    }
    }
    TRACE("(%p) returning %s\n", _this, _this->name);
    return _this->name;
    }

    /******************************************************************
    * ?raw_name@type_info@@QBEPBDXZ (MSVCRT.@)
    */
    DEFINE_THISCALL_WRAPPER0(MSVCRT_type_info_raw_name);
    const char * __stdcall MSVCRT_type_info_raw_name(type_info * _this)
    {
    TRACE("(%p) returning %s\n", _this, _this->mangled);
    return _this->mangled;
    }

    /* Unexported */
    DEFINE_THISCALL_WRAPPER1(MSVCRT_type_info_vector_dtor);
    void * __stdcall MSVCRT_type_info_vector_dtor(type_info * _this, unsigned int flags)
    {
    TRACE("(%p %x)\n", _this, flags);
    if (flags & 2)
    {
    /* we have an array, with the number of elements stored before the first object */
    int i, *ptr = (int *)_this - 1;

    for (i = *ptr - 1; i >= 0; i--) MSVCRT_type_info_dtor(_this + i);
    MSVCRT_operator_delete(ptr);
    }
    else
    {
    MSVCRT_type_info_dtor(_this);
    if (flags & 1) MSVCRT_operator_delete(_this);
    }
    return _this;
    }

    /* vtables */

    const exception_vtable MSVCRT_exception_vtable =
    {
    __thiscall_MSVCRT_exception_vector_dtor,
    __thiscall_MSVCRT_what_exception
    };

    const exception_vtable MSVCRT_bad_typeid_vtable =
    {
    __thiscall_MSVCRT_bad_typeid_vector_dtor,
    __thiscall_MSVCRT_what_exception
    };

    const exception_vtable MSVCRT_bad_cast_vtable =
    {
    __thiscall_MSVCRT_bad_cast_vector_dtor,
    __thiscall_MSVCRT_what_exception
    };

    const exception_vtable MSVCRT___non_rtti_object_vtable =
    {
    __thiscall_MSVCRT___non_rtti_object_vector_dtor,
    __thiscall_MSVCRT_what_exception
    };

    static const exception_vtable MSVCRT_type_info_vtable =
    {
    __thiscall_MSVCRT_type_info_vector_dtor,
    NULL
    };

    /* Static RTTI for exported objects */

    static type_info exception_type_info =
    {
    (void*)&MSVCRT_type_info_vtable,
    NULL,
    ".?AVexception@@"
    };

    static const rtti_base_descriptor exception_rtti_base_descriptor =
    {
    &exception_type_info,
    0,
    0,
    0,
    0,
    0
    };

    static const rtti_base_array exception_rtti_base_array =
    {
    {
    &exception_rtti_base_descriptor,
    NULL,
    NULL
    }
    };

    static const rtti_object_hierachy exception_type_hierachy =
    {
    0,
    0,
    1,
    &exception_rtti_base_array
    };

    static const rtti_object_locator exception_rtti =
    {
    0,
    0,
    0,
    &exception_type_info,
    &exception_type_hierachy
    };

    static const cxx_type_info exception_cxx_type_info =
    {
    0,
    &exception_type_info,
    0,
    -1,
    0,
    sizeof(exception),
    (cxx_copy_ctor)__thiscall_MSVCRT_exception_copy_ctor
    };

    static type_info bad_typeid_type_info =
    {
    (void*)&MSVCRT_type_info_vtable,
    NULL,
    ".?AVbad_typeid@@"
    };

    static const rtti_base_descriptor bad_typeid_rtti_base_descriptor =
    {
    &bad_typeid_type_info,
    1,
    0,
    0xffffffff,
    0,
    0
    };

    static const rtti_base_array bad_typeid_rtti_base_array =
    {
    {
    &bad_typeid_rtti_base_descriptor,
    &exception_rtti_base_descriptor,
    NULL
    }
    };

    static const rtti_object_hierachy bad_typeid_type_hierachy =
    {
    0,
    0,
    2,
    &bad_typeid_rtti_base_array
    };

    static const rtti_object_locator bad_typeid_rtti =
    {
    0,
    0,
    0,
    &bad_typeid_type_info,
    &bad_typeid_type_hierachy
    };

    static const cxx_type_info bad_typeid_cxx_type_info =
    {
    0,
    &bad_typeid_type_info,
    0,
    -1,
    0,
    sizeof(exception),
    (cxx_copy_ctor)__thiscall_MSVCRT_bad_typeid_copy_ctor
    };

    static type_info bad_cast_type_info =
    {
    (void*)&MSVCRT_type_info_vtable,
    NULL,
    ".?AVbad_cast@@"
    };

    static const rtti_base_descriptor bad_cast_rtti_base_descriptor =
    {
    &bad_cast_type_info,
    1,
    0,
    0xffffffff,
    0,
    0
    };

    static const rtti_base_array bad_cast_rtti_base_array =
    {
    {
    &bad_cast_rtti_base_descriptor,
    &exception_rtti_base_descriptor,
    NULL
    }
    };

    static const rtti_object_hierachy bad_cast_type_hierachy =
    {
    0,
    0,
    2,
    &bad_cast_rtti_base_array
    };

    static const rtti_object_locator bad_cast_rtti =
    {
    0,
    0,
    0,
    &bad_cast_type_info,
    &bad_cast_type_hierachy
    };

    static const cxx_type_info bad_cast_cxx_type_info =
    {
    0,
    &bad_cast_type_info,
    0,
    -1,
    0,
    sizeof(exception),
    (cxx_copy_ctor)__thiscall_MSVCRT_bad_cast_copy_ctor
    };

    static type_info __non_rtti_object_type_info =
    {
    (void*)&MSVCRT_type_info_vtable,
    NULL,
    ".?AV__non_rtti_object@@"
    };

    static const rtti_base_descriptor __non_rtti_object_rtti_base_descriptor =
    {
    &__non_rtti_object_type_info,
    2,
    0,
    0xffffffff,
    0,
    0
    };

    static const rtti_base_array __non_rtti_object_rtti_base_array =
    {
    {
    &__non_rtti_object_rtti_base_descriptor,
    &bad_typeid_rtti_base_descriptor,
    &exception_rtti_base_descriptor
    }
    };

    static const rtti_object_hierachy __non_rtti_object_type_hierachy =
    {
    0,
    0,
    3,
    &__non_rtti_object_rtti_base_array
    };

    static const rtti_object_locator __non_rtti_object_rtti =
    {
    0,
    0,
    0,
    &__non_rtti_object_type_info,
    &__non_rtti_object_type_hierachy
    };

    static const cxx_type_info __non_rtti_object_cxx_type_info =
    {
    0,
    &__non_rtti_object_type_info,
    0,
    -1,
    0,
    sizeof(exception),
    (cxx_copy_ctor)__thiscall_MSVCRT___non_rtti_object_copy_ctor
    };

    static type_info type_info_type_info =
    {
    (void*)&MSVCRT_type_info_vtable,
    NULL,
    ".?AVtype_info@@"
    };

    static const rtti_base_descriptor type_info_rtti_base_descriptor =
    {
    &type_info_type_info,
    0,
    0,
    0xffffffff,
    0,
    0
    };

    static const rtti_base_array type_info_rtti_base_array =
    {
    {
    &type_info_rtti_base_descriptor,
    NULL,
    NULL
    }
    };

    static const rtti_object_hierachy type_info_type_hierachy =
    {
    0,
    0,
    1,
    &type_info_rtti_base_array
    };

    static const rtti_object_locator type_info_rtti =
    {
    0,
    0,
    0,
    &type_info_type_info,
    &type_info_type_hierachy
    };

    /*
    * Exception RTTI for cpp objects
    */
    static const cxx_type_info_table bad_cast_type_info_table =
    {
    3,
    {
    &__non_rtti_object_cxx_type_info,
    &bad_typeid_cxx_type_info,
    &exception_cxx_type_info
    }
    };

    static const cxx_exception_type bad_cast_exception_type =
    {
    0,
    (void*)__thiscall_MSVCRT_bad_cast_dtor,
    NULL,
    &bad_cast_type_info_table
    };

    static const cxx_type_info_table bad_typeid_type_info_table =
    {
    2,
    {
    &bad_cast_cxx_type_info,
    &exception_cxx_type_info,
    NULL
    }
    };

    static const cxx_exception_type bad_typeid_exception_type =
    {
    0,
    (void*)__thiscall_MSVCRT_bad_typeid_dtor,
    NULL,
    &bad_cast_type_info_table
    };

    static const cxx_exception_type __non_rtti_object_exception_type =
    {
    0,
    (void*)__thiscall_MSVCRT___non_rtti_object_dtor,
    NULL,
    &bad_typeid_type_info_table
    };

    #endif /* __i386__ */


    /******************************************************************
    * ?set_terminate@@YAP6AXXZP6AXXZ@Z (MSVCRT.@)
    *
    * Install a handler to be called when terminate() is called.
    *
    * PARAMS
    * func [I] Handler function to install
    *
    * RETURNS
    * The previously installed handler function, if any.
    */
    terminate_function MSVCRT_set_terminate(terminate_function func)
    {
    MSVCRT_thread_data *data = msvcrt_get_thread_data();
    terminate_function previous = data->terminate_handler;
    TRACE("(%p) returning %p\n",func,previous);
    data->terminate_handler = func;
    return previous;
    }

    /******************************************************************
    * ?set_unexpected@@YAP6AXXZP6AXXZ@Z (MSVCRT.@)
    *
    * Install a handler to be called when unexpected() is called.
    *
    * PARAMS
    * func [I] Handler function to install
    *
    * RETURNS
    * The previously installed handler function, if any.
    */
    unexpected_function MSVCRT_set_unexpected(unexpected_function func)
    {
    MSVCRT_thread_data *data = msvcrt_get_thread_data();
    unexpected_function previous = data->unexpected_handler;
    TRACE("(%p) returning %p\n",func,previous);
    data->unexpected_handler = func;
    return previous;
    }

    /******************************************************************
    * ?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z (MSVCRT.@)
    */
    _se_translator_function MSVCRT__set_se_translator(_se_translator_function func)
    {
    MSVCRT_thread_data *data = msvcrt_get_thread_data();
    _se_translator_function previous = data->se_translator;
    TRACE("(%p) returning %p\n",func,previous);
    data->se_translator = func;
    return previous;
    }

    /******************************************************************
    * ?terminate@@YAXXZ (MSVCRT.@)
    *
    * Default handler for an unhandled exception.
    *
    * PARAMS
    * None.
    *
    * RETURNS
    * This function does not return. Either control resumes from any
    * handler installed by calling set_terminate(), or (by default) abort()
    * is called.
    */
    void MSVCRT_terminate(void)
    {
    MSVCRT_thread_data *data = msvcrt_get_thread_data();
    if (data->terminate_handler) data->terminate_handler();
    abort();
    }

    /******************************************************************
    * ?unexpected@@YAXXZ (MSVCRT.@)
    */
    void MSVCRT_unexpected(void)
    {
    MSVCRT_thread_data *data = msvcrt_get_thread_data();
    if (data->unexpected_handler) data->unexpected_handler();
    MSVCRT_terminate();
    }

    /* Get type info from an object (internal) */
    static const rtti_object_locator* RTTI_GetObjectLocator(type_info *cppobj)
    {
    const rtti_object_locator *obj_locator = NULL;

    #ifdef __i386__
    const exception_vtable* vtable = (const exception_vtable*)cppobj->vtable;

    /* Perhaps this is one of classes we export? */
    if (vtable == &MSVCRT_exception_vtable)
    {
    TRACE("returning exception_rtti\n");
    return &exception_rtti;
    }
    else if (vtable == &MSVCRT_bad_typeid_vtable)
    {
    TRACE("returning bad_typeid_rtti\n");
    return &bad_typeid_rtti;
    }
    else if (vtable == &MSVCRT_bad_cast_vtable)
    {
    TRACE("returning bad_cast_rtti\n");
    return &bad_cast_rtti;
    }
    else if (vtable == &MSVCRT___non_rtti_object_vtable)
    {
    TRACE("returning __non_rtti_object_rtti\n");
    return &__non_rtti_object_rtti;
    }
    else if (vtable == &MSVCRT_type_info_vtable)
    {
    TRACE("returning type_info_rtti\n");
    return &type_info_rtti;
    }
    #endif

    if (!IsBadReadPtr(cppobj, sizeof(void *)) &&
    !IsBadReadPtr(cppobj->vtable - 1,sizeof(void *)) &&
    !IsBadReadPtr((void*)cppobj->vtable[-1], sizeof(rtti_object_locator)))
    {
    obj_locator = (rtti_object_locator *)cppobj->vtable[-1];
    TRACE("returning type_info from vtable (%p)\n", obj_locator);
    }

    return obj_locator;
    }

    /******************************************************************
    * __RTtypeid (MSVCRT.@)
    *
    * Retrieve the Run Time Type Information (RTTI) for a C++ object.
    *
    * PARAMS
    * cppobj [I] C++ object to get type information for.
    *
    * RETURNS
    * Success: A type_info object describing cppobj.
    * Failure: If the object to be cast has no RTTI, a __non_rtti_object
    * exception is thrown. If cppobj is NULL, a bad_typeid exception
    * is thrown. In either case, this function does not return.
    *
    * NOTES
    * This function is usually called by compiler generated code as a result
    * of using one of the C++ dynamic cast statements.
    */
    type_info* MSVCRT___RTtypeid(type_info *cppobj)
    {
    const rtti_object_locator *obj_locator = RTTI_GetObjectLocator(cppobj);

    #ifdef __i386__
    if (!obj_locator)
    {
    static const char* szNullPtr = "Attempted a typeid of NULL pointer!";
    static const char* szBadPtr = "Bad read pointer - no RTTI data!";
    const cxx_exception_type *e_type;
    exception e;

    /* Throw a bad_typeid or __non_rtti_object exception */
    if (!cppobj)
    {
    EXCEPTION_ctor(&e, &szNullPtr);
    e.vtable = &MSVCRT_bad_typeid_vtable;
    e_type = &bad_typeid_exception_type;
    }
    else
    {
    EXCEPTION_ctor(&e, &szBadPtr);
    e.vtable = &MSVCRT___non_rtti_object_vtable;
    e_type = &__non_rtti_object_exception_type;
    }

    _CxxThrowException(&e, e_type);
    DebugBreak();
    }
    return obj_locator->type_descriptor;
    #else
    return NULL;
    #endif
    }

    /******************************************************************
    * __RTDynamicCast (MSVCRT.@)
    *
    * Dynamically cast a C++ object to one of its base classes.
    *
    * PARAMS
    * cppobj [I] Any C++ object to cast
    * unknown [I] Reserved, set to 0
    * src [I] type_info object describing cppobj
    * dst [I] type_info object describing the base class to cast to
    * do_throw [I] TRUE = throw an exception if the cast fails, FALSE = don't
    *
    * RETURNS
    * Success: The address of cppobj, cast to the object described by dst.
    * Failure: NULL, If the object to be cast has no RTTI, or dst is not a
    * valid cast for cppobj. If do_throw is TRUE, a bad_cast exception
    * is thrown and this function does not return.
    *
    * NOTES
    * This function is usually called by compiler generated code as a result
    * of using one of the C++ dynamic cast statements.
    */
    void* MSVCRT___RTDynamicCast(type_info *cppobj, int unknown,
    type_info *src, type_info *dst,
    int do_throw)
    {
    const rtti_object_locator *obj_locator;

    /* Note: cppobj _isn't_ a type_info, we use that struct for its vtable ptr */
    TRACE("(%p,%d,%p,%p,%d)\n", cppobj, unknown, src, dst, do_throw);
    if (!cppobj)
    return 0;
    obj_locator= RTTI_GetObjectLocator(cppobj);
    if (unknown)
    FIXME("Unknown parameter is non-zero: please report\n");

    /* To cast an object at runtime:
    * 1.Find out the true type of the object from the typeinfo at vtable[-1]
    * 2.Search for the destination type in the class heirachy
    * 3.If destination type is found, return base object address + dest offset
    * Otherwise, fail the cast
    */
    if (obj_locator)
    {
    int count = 0;
    const rtti_object_hierachy *obj_bases = obj_locator->type_hierachy;
    const rtti_base_descriptor* const *base_desc = obj_bases->base_classes->bases;
    int src_offset = obj_locator->base_class_offset, dst_offset = -1;

    while (count < obj_bases->array_len)
    {
    const type_info *typ = (*base_desc)->type_descriptor;

    if (!strcmp(typ->mangled, dst->mangled))
    {
    dst_offset = (*base_desc)->base_class_offset;
    break;
    }
    base_desc++;
    count++;
    }
    if (dst_offset >= 0)
    return (void*)((unsigned long)cppobj - src_offset + dst_offset);
    }

    #ifdef __i386__
    /* VC++ sets do_throw to 1 when the result of a dynamic_cast is assigned
    * to a reference, since references cannot be NULL.
    */
    if (do_throw)
    {
    static const char* exception_text = "Bad dynamic_cast!";
    exception e;

    /* Throw a bad_cast exception */
    EXCEPTION_ctor(&e, &exception_text);
    e.vtable = &MSVCRT_bad_cast_vtable;
    _CxxThrowException(&e, &bad_cast_exception_type);
    DebugBreak();
    }
    #endif
    return NULL;
    }


    /******************************************************************
    * __RTCastToVoid (MSVCRT.@)
    *
    * Dynamically cast a C++ object to a void*.
    *
    * PARAMS
    * cppobj [I] The C++ object to cast
    *
    * RETURNS
    * Success: The base address of the object as a void*.
    * Failure: NULL, if cppobj is NULL or has no RTTI.
    *
    * NOTES
    * This function is usually called by compiler generated code as a result
    * of using one of the C++ dynamic cast statements.
    */
    void* MSVCRT___RTCastToVoid(type_info *cppobj)
    {
    const rtti_object_locator *obj_locator = RTTI_GetObjectLocator(cppobj);

    /* Note: cppobj _isn't_ a type_info, we use that struct for its vtable ptr */
    TRACE("(%p)\n", cppobj);

    /* Casts to void* simply cast to the base object */
    if (obj_locator)
    return (void*)((unsigned long)cppobj - obj_locator->base_class_offset);
    return NULL;
    }
    /*

    • Плюс 2
  2. Хочу предложить, чтобы ссылки для скачивания, скрины и коды были доступны только для зарегестрированных пользавателей :grin: Таким образом и пользователей больше будет :P

    Это принуждение. Не очень хороший способ. Я бывал на таких форумах, впечатление они оставляют не лучшее: "мы вам покажем то-то, если вы зарегистрируетесь". А если я не решил ещё регистрироваться, зачем скрывать то, что обычно доступно :)

  3. Возможно иногда я буду писать о приёмах или кто-то решит написать здесь свой или рассмотреть чужие.

    Концепция которую я рассмотрю первой касается реализация динамического списка объектов на Дельфи.

    Реализация динамического списка объектов.

    Делать мы будем такую штуку,

    screenbn.png

    а затем я напишу где ещё можно применить динамические списки. На рисунке форма программы. Если мы нажимаем на кнопку мышки, то создаётся объект точки и добавляется в список. Действия ниже абсолютно не сложные.


    TMyPixel = class(TObject)
    FX: Integer;
    FY: Integer;
    FText: Integer;
    constructor Create(X, Y, Num: Integer);
    procedure SetPixel;
    end;

    procedure TMyPixel.SetPixel;
    begin
    MainForm.Canvas.PolyLine([Point(FX, FY), Point(FX, FY)]);
    MainForm.Canvas.TextOut(FX + 1, FY + 1, IntToStr(FText));
    end;

    procedure TMainForm.SortBtnClick(Sender: TObject);
    var i: Integer;
    begin
    PixList.Sort(PixCompare); // PixList : TList;
    with PixList do
    for i := 0 to Count - 1 do TMyPixel(Items[i]).FText := i + 1;
    end;

    Заметка.

    Мы можем написать свой динамический список.

    Принцип крутится вокруг

    setlength(имя массива элементов, размер массива)

    и

    массив[индекс]:= значение

    Это принцип необходимо обернуть в класс и добавить методы: добавление/удаление/вставка/поменять местами и т.п. Каждый раз проверять длину массива length(массив) или sizeof(массив) и если необходимо расширять массив или фрагментировать пустоты в нём при удалении элемента внутри массива.

    Существует стандартный класс TObject и класс TList. TList может содержать список объектов. В объект можно обвернуть очень многое.

    Если вам нужно то ознакомьтесь с описанием этих классов.

    Описание TObject

    Тип TObject определяет тип базового класса. Он является самым старым прародителем всех классов - каждый класс, в конечном счете, получен из TObject.

    Из-за этого, каждый объект унаследовал методы TObject.

    Методы TObject относятся к двум категориям - класс и не класс. Когда предустановленно ключевое слово Class, метод можно вызвать и в объекте класса, и в классе непосредственно. Такой статический метод не может обратиться ни к каким полям класса, потому что сам класс не имеет никаких данных, только объекты - имеют данные.

    Некоторые ключевые (статические) методы Class:

    function ClassName Выдает имя класса как строку

    ClassParent Выдает имя родителя класса

    ClassInfo Выдает Run Time информацию класса

    InstanceSize Размер объекта класса в байтах

    NewInstance Создает новый объект класса

    Некоторые ключевые методы Object:

    Create Создатель пустого объекта

    Free Вызывает Destroy для ненулевых объектов

    Destroy Высвобождение памяти объекта

    AfterConstruction Вызывается после построения

    BeforeDestruction Вызывается перед разрушением

    Описание TList

    Класс TList очень полезный универсальный контейнер списков. Он отличается от массивов, в которых он обеспечивает более богатые функциональные возможности.

    В частности объекты TList могут быть отсортированы. Эта сортировка может быть с использованием любых выбранных критериев. Например, список может содержать набор объектов, которые имеют строку и численные поля. Вы можете отсортировать список по строке, по числу, по обоим, с возрастанием или убыванием, как Вы желаете. И пересортировать позже по другим критериям.

    Показанный пример кода показывает такую сортировку.

    Ключевые свойства и методы упомянуты ниже.

    Свойство Capacity

    Используется для установления размера (число указателей на объекты) списка. Предварительно установив в разумное значение, можно избежать множественных перераспределений памяти.

    Свойство Count

    Число элементов (указателей) в списке. Может быть прочитано или записано. Если размер уменьшен в результате изменения значения Count, то удаляются элементы в конце списка.

    Свойство Items

    Позволяет обращаться к элементам в списке. Например, myList.Items[2] ; возвращает 3-ий элемент в списке. Это свойство, заданное по умолчанию, вышеупомянутое может быть упрощено до myList[2];.

    Свойство List

    Возвращает элементы в массиве.

    Метод Add

    Добавляет элемент в конец списока.

    Метод Assign

    Заменяет список содержанием другого списка.

    Метод Clear

    Удаляет все элементы списка, устанавливая Count в 0.

    Метод Delete

    Удаляет элемент из списка по его позиции в списке.

    Метод Remove

    Удаляет элемент из списка по его объектному указателю.

    Метод Exchange

    Меняет позиции двух элементов

    Метод Move

    Перемещает элемент в новую позицию списка.

    Метод Insert

    Вставляет новый элемент в список в данную позицию.

    Метод First

    Получает первый элемент в списке.

    Метод Last

    Получает последний элемент в списке.

    Метод Sort

    Сортирует список в соответствии с вашими указанными критериями. Сортировка списка проводится внутри TList, но каждая пара элемента сравнивается, вызывая функцию, которую вы указали для этого метода.

    Метод IndexOf

    Выдает позицию указанного объекта в списке.

    Полный код:



    interface

    uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls, Buttons;

    type
    TMainForm = class(TForm)
    ListBtn: TBitBtn;
    ClearBtn: TBitBtn;
    DelBtn: TBitBtn;
    SortBtn: TBitBtn;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
    Shift: TShiftState; X, Y: Integer);
    procedure ListBtnClick(Sender: TObject);
    procedure ClearBtnClick(Sender: TObject);
    procedure DelBtnClick(Sender: TObject);
    procedure SortBtnClick(Sender: TObject);
    private
    PixList: TList;
    PixNum: Integer;
    public
    { Public declarations }
    end;

    TMyPixel = class(TObject)
    FX: Integer;
    FY: Integer;
    FText: Integer;
    constructor Create(X, Y, Num: Integer);
    procedure SetPixel;
    end;


    var
    MainForm: TMainForm;

    implementation

    {$R *.dfm}

    const PixColor = clRed;

    var CurPixel: TMyPixel;

    constructor TMyPixel.Create(X, Y, Num: Integer);
    begin
    inherited Create;
    FX := X;
    FY := Y;
    FText := Num;
    SetPixel;
    end;

    procedure TMyPixel.SetPixel;
    begin
    MainForm.Canvas.PolyLine([Point(FX, FY), Point(FX, FY)]);
    MainForm.Canvas.TextOut(FX + 1, FY + 1, IntToStr(FText));
    end;

    function PixCompare(Item1, Item2: Pointer): Integer;
    var Pix1, Pix2: TMyPixel;
    begin
    Pix1 := Item1;
    Pix2 := Item2;
    Result := Pix1.FX - Pix2.FX;
    end;

    procedure TMainForm.FormCreate(Sender: TObject);
    begin
    PixList := TList.Create;
    PixNum := 1; {Счетчик точек}
    Canvas.Pen.Color := PixColor; {Цвет точки}
    Canvas.Pen.Width := 3; {Размер точки}
    Canvas.Brush.Color := Color; {Цвет фора текста равен цвету формы}
    end;

    procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
    PixList.Free;
    end;

    procedure TMainForm.FormMouseDown(Sender: TObject; Button: TMouseButton;
    Shift: TShiftState; X, Y: Integer);
    begin
    PixList.Add(TMyPixel.Create(X, Y, PixNum));
    Inc(PixNum);
    end;

    procedure TMainForm.ListBtnClick(Sender: TObject);
    var i: Integer;
    begin
    with PixList do
    for i := 0 to Count - 1 do
    begin
    CurPixel := Items[i];
    CurPixel.SetPixel;
    end;
    end;

    procedure TMainForm.ClearBtnClick(Sender: TObject);
    begin
    Canvas.FillRect(Rect(0, 0, Width, Height));
    end;

    procedure TMainForm.DelBtnClick(Sender: TObject);
    begin
    PixList.Clear;
    PixNum := 1;
    end;

    procedure TMainForm.SortBtnClick(Sender: TObject);
    var i: Integer;
    begin
    PixList.Sort(PixCompare);
    with PixList do
    for i := 0 to Count - 1 do TMyPixel(Items[i]).FText := i + 1;
    end;

    end.
    unit uMain;

    Скачать исходники: ссылка

    Теперь поговорим что даст список объектов.

    Первое - это удобно. Вам не нужно создавать свои классы.

    Второе - каждый объект в списке может иметь дочерний объект или родительский. Если один объект разрушается, можно задать разрушение всех дочерних. Вы можете составить список объектов сложной иерархии и через цикл обрабатывать их действия между собой. Можно сказать что я подразумеваю прототип игровых объектов. Они могут толкаться между собой, драться, разрушать делать что-то по определённому алгоритму, получать внешнее воздействие и возможно даже ограниченно приспосабливаться под это воздействие. :)

  4. Давно у нас не было статей и вот пришло время поучаствовать с автором во обмане игры. Статью я откопал случайно, она 2002 года, она покажет как искать цепочку указателей.

    Есть некоторые моменты касательно реверсинга. Например, как и когда и какие бряки нужно ставить применительно к СофтАйс. Тоже самое можно сделать и в OllyDbg. Главное понять технику.

    Указатель будет вот такого вида:

    @(@(@(@(@(@(@(@(862360)+4)+4)+10074))+0C2*4)+4)+A8)

    надеюсь без труда вы сможете это представить скобками как мы делаем:

    [[[[[[[[862360]+4]+4]+10074]]+0C2*4]+4]+A8] = здоровью (8 пар скобок!)

    Статья на мой взгляд написана "кусками", но кое-что понять можно. Для тех кто с СофтАйсом не работал, то выражение к примеру

    mov eax,[eax][esi]*4 

    эквивалентно для OllyDbg:

    mov eax,[eax+esi*4]

    Итак статья.

    Автор: FreeExec

    Выбирать особо было не из чего, т.к. на машине стояла только одна игра Newer Winter Nigths. И решено было писать тренер к ней.

    Продолжение статьи...

    Трейнер это программа, которая позволяет изменять значения в игре, например жизни. Сам я читеров не люблю и втихомолку сильно бью *(. Но так я думал до поры до времени, пока не припёрло к стенке, т.е. к монстру. И набросал я быстренько (ну это как посмотреть) свой трейнер.

    С помощью ArtMoney я нашел тот блок памяти, в котором хранятся очки жизни. Было бы всё просто, если бы не одно НО: при каждой новой загрузке это место меняется, это происходит по тому, что программа выделяет память динамически и раз на раз не приходится. Ну, раз программа находит нужное место то и я его смогу найти.)

    Примечание: ArtMoney найдёт не одно значение, у меня их было 10. Меняйте эти значения и смотрите за остальными, некоторые ячейки тоже поменяют своё значение на введённое вами. Та ячейка, которая изменяет собой больше ячеек и есть искомая.

    Запускаем SoftICE, и ставил брейкпоинт (в дальнейшем БР) на доступ к памяти в ячейку, которую мы нашли. (У меня по адресу 10AEFC70, поэту команда для SoftICE'а выглядит так: bpmd 10AEFC70. Но обратите внимание что под окном кода должно быть написано, что вы в модуле nwmain.text, иначе у вас не чего не выйдет). Жмём F5, когда выскочит SoftICE.

    Это должна быть инструкция по адресу 607C87 MOV AX, [ECX+A8]. Т.е. какое-то значение (ECX)+А8 ссылается на жизнь. Следующим шагом нужно отыскать то место где регистр ECX получает своё значение. Чуть ниже этой инструкции стоит команда RET- это выход из функции. Выполняем пошагово (F8) пока не выйдем из функции, и не уведем следующее:


    5D05E3 call [edx+00000009C]
    5D05E1 mov ecx, esi

    Ставим БР по адресу 5D05E1 командой "bpx 5D05E1" или двойным щелчком на строке и нажимаем F5 несколько раз, пока ESI не будет равен найденному значению минус A8 (10AEFC70-A8=10AEFBC8). Двигаем код в окне вверх (CTRL+вверх), ища, где ESI получает своё значение, должны дойти вот досюда:


    005D0073 call [edx+00030]
    005D0076 mov esi,eax
    005D0071 mov ecx,ebx

    Значит, функция возвращает в регистре EAX наше число, но не стоит сразу ковырять внутренности функции, поставив БР по адресу 005D0071, мы увидим, что EBX=10AEFBC8. Чуть выше видим:

    005D0049 mov ebx,[esp+00050]

    Снова ставим БР на участок памяти ESP+50 "bpmb esp+20 w"(w означает, что отслеживать только запись по этому адресу). Наберём в командной строке "? ESP+50" чтобы получить точный адрес, затем "d ESp+50" чтобы видеть этот участок. Жмём F5 столько раз, пока на этом участке не появится наше значение в перевёрнутом виде, т.е. C8FBAE10 (руки бы оторвать тому, кто это придумал %). Как дальше разворачивались дела я не помню, но вы должны выйти на:

    CALL 005CF000 (например, по адресу 5D8D6A, 5D8866)

    После выполнения этой функции EAX содержит наше число. Значит способ получения этого числа в этой функции. Вот содержимое этой функции (примечание: все числа после точки с запетой приведены конкретно для моего примера, у вас они могут быть другие):


    mov eax,ecx
    mov ecx,[000862360] ; ECX=A6BA590 (7)
    mov edx,[eax][00030]
    mov ecx,[ecx][00004] ; ECX=[A6BA594]=79A6058 (6)
    push edx
    call .0005BB7E0

    5BB7E0:
    mov eax,[esp][00004]
    mov ecx,[ecx][00004] ; ECX=[79A605C]=7A58008 (5)
    push eax
    call .0005C2B10

    5C2B10:
    push ecx
    mov edx,[esp][00008]
    mov ecx,[ecx][000010074] ; ECX=[7A6807C]=79F1CB8 (4)
    lea eax,[esp][00000]
    push eax
    push edx
    mov d,[esp][00008],000000000
    call .0004259D0

    4259D0:
    mov eax,[esp][00004] ; EAX=20C2
    push esi
    mov esi,eax
    shr esi,01F ; ESI=0
    and esi,001
    mov edx,eax
    and eax,000000FFF ; EAX=0C2
    shl esi,00C ; ESI=0
    add esi,eax ; ESI=0C2
    mov eax,[ecx] ; EAX=[79F1CB8]=7A1EFF8 (3)
    mov eax,[eax][esi]*4 ; EAX=[7A1F300]=7BD1E08 (2)
    and edx,07FFFFFFF
    test eax,eax
    pop esi
    je .000425A04
    cmp [eax],edx
    je .000425A13
    mov eax,[eax][00008]
    test eax,eax
    jne .0004259F9
    mov ecx,[esp][00008]
    mov d,[ecx],000000000
    mov al,001
    retn 00008
    mov edx,[eax][00004] ; EDX=10AEFBC8 (1)
    5CF000:

    Цифрами в скобках обозначены ключевые моменты кода. Мы видим, чтобы получить нужный адрес надо прочитать DWORD по адресу EAX+4. В свою очередь этот EAX=EAX+ESI*4, где ESI=0C2. И так далее. Чтобы проверить правильность наших рассуждений пишем в командной строке SoftICE'а "? @(10AEFBC8+A8)", программа должна написать текущее количество жизни (первое число в шестнадцатеричное, а вот второе десятичное). Теперь надо заменить число 10AEFBC8 на то, что мы получили, т.е. на @(7BD1E08+4). Соединяем все вместе - должно получится "? @(@(7BD1E08+4)+A8)". Аналогичным способом движемся выше до строки обозначенной (7), там адрес задан конкретно. Он будет всегда один и тем же. В итоге у меня получилось так: "? @(@(@(@(@(@(@(@(862360)+4)+4)+10074))+0C2*4)+4)+A8)". Вот в принципе и всё что касается нахождения нашей жизни ;) Можно уже юзать это совместно с SoftICE'ом.

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

    Чтобы иметь возможность изменять данные в чужом процессе (программе) нужно знать её идентификатор процесса. Его можно получить используя функцию NtQuerySystemInformation(5, адрес_буфера, размер_буфера, 0), из библиотеки ntdll.dll. Чем больше процессов запущено, тем больше нужен буфер. У меня он составляет 7 Кб. Буфер состоит из массива структур ProcessInfo. По 0 смещению задан размер структуры DWORD. А со смещением 3C ссылка на имя процесса в формате UniCODE. По смещению 44 находится идентификатор процесса. Затем открываем этот процесс функцией OpenProcess(1F0FFF,0,идентификатор_процесса), она возвратит нам хендл на этот процесс. С помощью функции ReadProcessMemory(Handle, адресс_в_процессе_ откуда_читать, адресс_буфера_ куда_записать, сколько_прочитать, 0) сначала читаем по адресу 862360, прибавляем к этому значению 4 и снова читаем по полученному адресу, и т.д. Запись в память производится с помощью WriteProcessMemory(Handle, адрес_куда_записывать, адрес_буфера, сколько_записать, 0).

    Процесса заморозки можно добиться, используя вызов по таймеру. Создаём таймер - SetTimer(hWnd-идентификатор_окна, уникальный_номер, время_в_милисек, 0 или адрес_процедуры_ которая_будет_вызываться). Если последний аргумент равен 0, то процедуре обработки окна будет приходить сообщение WM_TIMER, если таймеров много то отличить их можно по уникальному номеру, который был задан при создании таймера, он находится в переменной wParam. Не забудьте удалить таймер при выходе KillTimer(hWnd, handle).

    Ну, вот в принципе и всё, осталось нарисовать морду будущей проги и описать несколько функций.

    P.S. Функция NtQuerySystemInformation доступна только NT системам в Win9x нужно юзать CreateToolhelp32Snapshot, Process32First, Process32Next чтобы получить идентификатор нужного процесса.

  5. По поводу первого, я хоть и вхожу в группу администраторов, но в форумских движках не копался да некогда разбираться, к сожалению.

    По поводу второго это не удобно. Удерживай Shift и переходи по ссылкам. Ещё вариант - средняя кнопка мышки.

  6. ....

    4. Должно помочь, если не помогло, то переустанови другую версию Икс Пи 8-)

    Не разобравшись в проблеме переустанавливать Дельфи, а также операционку не стоит. Возможно, проще начать разобираться с кодировками в настройках Дельфи, а затем разбираться с кодировкой в ОС-ме в связке с кодировкой в Дельфи. Подходят ли кодировки, почему не подходят?! Кому сильно докучает эта проблема, могу попробовать поискать решение проблемы. Переустанавливать Дельфи и тем более ОС не стоит. Попробую ещё раз без лени решить эту проблему, если решу, то отпишусь....

    Посмотрел я. У меня этой проблемы нет почему-то. Сама как-то отпала )

  7. 1. Впорос. По нажатию мышки на элементе полученного с сервера его данные, т.е. целая строка должна передаться в поле ввода. Перечитал туторы не очень понимаю, как это сделать.

    Я эту задачу решил и позже дам ответ.

    А вторую задачу ещё можно обсудить =)

    PS Советую любителя javascript зайти на сайт и узнать инфу по работе с событиямисайт =)

  8. Спасибо SER[G]ANT.

    Кстати чтобы тебя цитировать приходиться убирать скобки SER[G]ANT :)

    Так тоже можно (?)


    var
    x,y: Integer;
    begin
    for y := 0 to Form1.Height
    do for x := 0 to Form1.Width
    do if (y mod 5 = 0) and (x mod 5 = 0)
    then Form1.Canvas.Pixels[x,y] := clBlack;
    end;
    procedure TForm1.FormPaint(Sender: TObject);

    Form1.Height и Form1.Width это не клиентская область поэтому будут некоторые смещения у сетки.

    Когда в цикле делаешь do for x := 0 to Form1.Width, то возможно (я точно не помню) это будет сказываться на производительности, т.к. каждый раз будет расчитываться путь до значения "with" через смещение от "Form1." Этот цикл сравнения if (y mod 5 = 0) and (x mod 5 = 0), тоже влияется на производительность. Но я только предполагаю, надо лесть в отлдаку и сравнивать производительность. :)

  9. task.png

    Вводим в поле слово и похожие слова динамически подгружаются с IIS сервера по запросу который я составил

            <asp:AccessDataSource ID="AccessDataSource1" runat="server" 
    DataFile="~/App_Data/test.mdb"
    SelectCommand="SELECT [fsText] FROM [tblRecords] WHERE ([fsText] LIKE ? + '%')"
    onselecting="AccessDataSource1_Selecting">
    <SelectParameters>
    <asp:Parameter Name="fsText" Type="String" />
    </SelectParameters>
    </asp:AccessDataSource>

    1. Впорос. По нажатию мышки на элементе полученного с сервера его данные, т.е. целая строка должна передаться в поле ввода. Перечитал туторы не очень понимаю, как это сделать.


    <input id="Text1" type="text" onkeyup="Send()"/></div>
    <table class="Table">
    <tr><td onclick="ClickTD(this)"><span class='FontSearch'>Животное1</span> на крыше</td></tr>
    <tr><td onclick="ClickTD(this)"><span class='FontSearch'>Животное2</span> на крыше</td></tr>
    </table>

    Вызываем функцию ClickTD(this)

        function ClickTD(e) {   
    v = e.toString();
    document.getElementById("Text1").value=v;
    }

    В общем идёт не значение поля которое вызывало функцию, а что-то другое. Есть у гугла такая штука тоже, но я не разобрался.

    2. Впорос. Сетевой трафик.

    Умные люди пишут, что всячески нужно стараться снимать нагрузку с машины сервера и уменьшать трафик обмена. Если посмотреть на рисунок сверху, то видно что получение данных может быть двумя способами.

    1) Вот эта разметка полностью строиться на сервере и динамически (без перезагрузки всей страницы) передаётся клиенту

    <input id="Text1" type="text" onkeyup="Send()"/></div>
    <table class="Table">
    <tr><td onclick="ClickTD(this)"><span class='FontSearch'>Животное1</span> на крыше</td></tr>
    <tr><td onclick="ClickTD(this)"><span class='FontSearch'>Животное2</span> на крыше</td></tr>
    </table>

    где каждое животное берём из базы.

    2) Способ не формировать эту разметку на сервере, а скинуть не форматированные данные...

    Животное1@
    Животное2@

    ...клиенту в функцию, которая построит всю разметку и динамически встроит её в тег div-а (pop-up) панели.

    Первый способ очень простой на C# и удобен и гораздо лучше соблюдается интеграция в другие проекты с приминением ООП C#. Также код формирования разметки закрыт от сторонних глаз. В этом случае те кто захотят такое же формирование разметки им придётся самим писать код или обращаться на мой сервер чтобы получить разметку снова.

    Итак плюсы первого способа: интеграция ООП, закрытый код.

    Минусы: увеличение нагрузки на сервер и увеличение трафика, клиенту дольше ждать ответа с сервера вместо того чтобы самому обработать скрипт.

    Про второй способ, когда разметка формируется на клиенте.

    Плюсы для меня: меньшая нагрузка на сервер, меньше трафика.

    Минусы: код javascript менее удобен как в написании так и в интеграции с будущеми поектами на ООП C#.

    С этим разумный вопрос не ужели нужно учитывать все плюсы и минусы двух способов или нужно придерживаться жёстко второго способа. Короче что выбрать и как?

  10. Можно взять такой вариант.

    unit _Paint;

    interface

    uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls;

    type
    TForm2 = class(TForm)
    procedure FormPaint(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    private
    { Private declarations }
    public
    { Public declarations }
    end;

    var
    Form2: TForm2;
    limitX: integer;
    limitY: integer;
    _Canvas: TCanvas;
    _Color: TColor;

    implementation

    {$R *.dfm}

    procedure TForm2.FormCreate(Sender: TObject);
    begin
    limitX:=10;
    limitY:=10;
    _Canvas:=Form2.Canvas;
    _Color:=0;
    end;

    procedure TForm2.FormPaint(Sender: TObject);
    var
    aWidth: integer;
    aHeigth: integer;
    i,j:integer;
    begin
    aWidth:=Form2.ClientWidth div limitX;
    aHeigth:=Form2.ClientHeight div limitY;

    for i:=0 to aWidth do
    for j:=0 to aHeigth do
    _Canvas.Pixels[i*limitX,j*limitY]:=_color;
    end;

    end.

    Правда есть и более производительный вариант с так называемой "подложкой" - картинокой. Подложка должна рисоваться внутри TForm2.FormPaint на самой Form2а изменяться внутри TForm2.OnResize.

    grid.png

  11. У нас появился закрытый раздел "Наши разработки". Делается это для того чтобы нашими исходными кодами не пользовались без спросу или хотя бы ограничить это пользование.

    В этот раздел можно входить под паролем или без пароля для группы пользователей "Разработчики". Этот статус очень почётный.

    В этой теме будут различные новости и информация о ходе совместных работ по разработке программ связанных с гейм хакингом. В частности это ТрейнерМакс(Delphi), Движок-Автоматических Иньекций(Delphi), Исследовательский движок дизассемблированного текста для встраивания в наш форум (Javascript). Последний нам позволит обмениваться опытом или подсказывать друг другу выходы из различных ситуаций и совместно анализировать тот или иной код.

    Участники могут помогать в изменении кода проектов, исправлении ошибок, создание своего кода с заданной задачей или предложить ту или иную свою идею.

    Те кто принял участие в модернизации CE автоматически войдут в группу Разработчиков. Остальные желающие получить доступ в скрытый раздел могут написать администрации личное сообщение. Тем кто учится программировать, тоже рекомендуется вступить в группу и влиться в общую деятельность по мере того как вам будет позволять время и желание.

    • Плюс 1
  12. У меня бывала такая ситуация, когда с английской раскладкой копируешь в буфер обмена исходный код в котором русские комментарии. В этом случае нужно изменить раскладку перед копированием. С другими случаями не сталкивался.

    В других случаях глянь настройку шрифтов Дельфи.

  13. Наброски что-то типа этого, но должны быть варианты и по эффективнее:

    newmem:
    push eax
    mov eax,[pPlayer]
    mov eax,[eax+14]
    mov eax,[eax+30] //для игрока eax=x, eax+4=y, eax+8=z

    push ebx
    push ecx
    mov ebx,[LocateCoord]
    mov ecx,[IndexLoc]
    lea ebx,[ebx+ecx]//[LocateCoord]+[IndexLoc]=X

    mov ecx,[ebx]
    mov [eax],ecx
    mov ecx,[ebx+4]
    mov [eax+4],ecx
    mov ecx,[ebx+8]
    mov [eax+8],ecx

    pop ecx
    pop ebx
    pop eax

    dd pPlayer
    dd pPlayerCoord
    dd LocateCoord
    dd IndexLoc

    • Плюс 1
  14. А зачем вообще делать фильтр, если есть рабочая цепочка указателей? В любую частовыполняемую инструкцию достаточно вставить запись нужного здоровья по этой цепочке и все.

    Ну так требуется one hit kill :) Делается в инструкции типа: B, C(B ). Без фильтра никак.

    Хотя есть вариант при one hit kill, чтобы не писать фильтр (как я мог не предвидеть раньше). Надо найти инструкцию типа A для здоровья врагов, обращение к которой происходит при стрельбе в них. Тогда фильтра действительного писать не нужно.

  15. Я не разбирал - пока некогда.

    Разбираться стоит с самого начала. Когда включаем этот режим в настройках, то должен учитываться флаг этого режима. А дальше через поиск по всем исходникам в среде Дельфи можно найти все условности связанные с этим флагом. В данном случае связанные с автоассемлированием и вообще записью в память и чтения из неё.

  16. 1. Найти/проверить инструкцию типа А.

    Предпологаемая инструкция (по вашим данным):

    game.exe+231B0D - fld dword ptr [eax+08]

    Поставить определение адресов на этой инструкции. Если на ней проскакивают адреса врагов значит это тип не А.

    Поставить бряк на адресе здоровья врага, получить инструкции (в частности при стрельбе во врага...)

    Сравнить инструкции при бряке на адресе здоровья своего героя с инструкциями при бряке на адресе врага и найти инстуркцию типа А.

    Если не нашли типа А, то идём вверх по указателю на один уровень и точно также ищем тип А ставив бряк на указатели своего героя и врага.

    Если не нашли, то опять идём вверх уже на конечный третий уровень пока не найдём тип А.

    На последнем уровне точно должна быть типа А.

    2. После нахождения инструкции типа А, нужно её изменить инъекцией чтобы

    сохранить указатель на некоторый уровень.

    [[[{3ий уровень}адрес+смещение]{2ой уровень}+смещение]{1ый уровень}+смещение] // {комментарии}

    Допустим "вынесли" (т.е. сохранили для фильтра) указатель с третьего уровня.

    3. В инструкции работающей непосредственно со здоровьем, т.е с 1-ым уровнем нужно сделать инъекцию кода,

    так чтобы 3-тий уровень был в качестве фильтра.

    Фильтр например напишем так

    "Если [[{3ий уровень}адрес+смещение]{2ой уровень}+смещение] = 1ый уровень, то записать здоровье своему игроку."

    Это самый простой способ, но с новыми патчами может оказаться не рабочим.

    Примечение.

    Для увелечения работоспособности:

    - поиск адреса внедрения через проверочные байты;

    - (CE на сегодняшний момент этого делать не умеет через так называемое автодизассемблирование)

    идентификация смещений в уровнях цепи указателей перед автоассемблированием,

    идентификация перед автоассемблированием регистров левой и правой частей инструкции по адресу внедрения, т.е. операндов источника и адресата. И конечный реультат - автоассемблирование с сгенериованным скриптом по указанным правилам: писать здоровье своему игроку и OneHitKill.

  17. Сделать полную сохранялку конкретной или любой игры дело не простое.

    Как минимум требует многочасовой работы и экспериментов.

    Но есть несколько возможных альтернативных вариантов.

    1. Использовать эмулятор Windows.

    2. Поискать программу в интернете.

    3. Поговорить с разработчиками Артмани, которые за это дело требуют деньги.

    4. Написать Дарк Байту (автору CheatEngine). Написать ЭльСпиро (разработчику MHS).

    Ещё есть разработчики WGC,DS - но они врядли этим будут заниматься.

    5. Попробовать каким-то изворотливым способом через Artmoney, экспериментируюя отключая звук, замораживая или удаляя какие-то потоки процесса через системыне утилиты.

  18. Может пуля за раз отнимает все жизни, но это врядли. Скорее всего где-то есть либо ещё одна структура повреждений, либо флаг смерти какой-то.

    Почему приведённый скрипт защищает от воды, но не защищает от пуль?

    А главное - почему при включении спидхака скрипт и от воды перестаёт защищать? Обновлять значения не успевает чтоли..

    Возможные причины не работающего бессметрия:

    1. Не найден адрес здоровья или найдены не все (что скорее всего).

    Проверить это можно, ставив бряк на запись на адреса здоровья своего героя и на адреса здоровья других игроков.

    2. Адрес (или адреса) здоровья может находиться в структуре1. С неё снимается копия в структуру2 и код который работает с этой структурой может повлиять на смерть героя.

    Больше вариантов у меня нет, если не тратить время чтобы придумывать их. Как написал Xipho необходимо смотреть в отладке. Хорошо бы найти несколько структур и постарать найти адреса-зацепки которые изменяются во время смерти некотрого игрока, но не своего...

    У меня пока только одна идея - в игре есть редактор миссий (в демке его нет), в этом редакторе есть скриптовая команда setdammage. Если бы придумать как вызвать эту команду во время игры, без редактора, можно было бы сделать бессмертие. Но как её вызвать я не понял(( либо искать причину гибели от пуль в другом месте

    Если setdammage работает в редакторе, то надо проследить в какую функцию идёт эта комманда. Попытаться найти её в самой игре. Я думаю, проще искать причину гибели от пуль.

    Это делать надо в Olly и брякнуться в момент оповещения о сметри героя. Именно брякнуться. Запомнить это место - Бряк1. Посмотреть стек вызовов и на каждом вызове поставить бряки. Перезапустить игру и играть если прерываемся и очевидной смерти нет, то снимаем бряки кроме 1. Если остался бряк1, то надо чё-то думать.

    И наконец когда смерть вновь произошла и сработал один из бряков, то плясать уже на этом уровне кода и смотреть что там происходит.... В общем не знаю как объяснить и предположить иное.

  19. В разработке

    Периодически этот действующий список буду пополнять,а когда функций накопится, то выложу.

    Cheat Engine 5.6 RUS (2.0)

    Добавления:

    1. Более удобная работа со скриптами в таблице CE: можно редактировать несколько скриптов подряд и корректно сохранять

    2. В автоасемблере можно вводить имя будущего скрипта или исправлять имя редактируемого скрипта (помог SERGANT)

    3. Звуковые уведомления:

    -Звук при завершении сканирования

    -Звук при нажатии на хоткей относительно таблицы CE

    -Звук при нажатии на хоткей отностильено созданного трейнера

    -Можно поменять файлы звукового оповещения ("Удачное событие", "Ошибка")

    4. Некоторые добавления:

    -"numeric" тебперь обозначаются как "NUMPAD"(помог SERGANT)

    - Добавлено контекстное меню в кнопку "Т"(SERGANT)

    - Изменён интерфейс "Создателя трейнера"(SERGANT)

    Баги:

    1. Работает поиск значений после поиска указателей. (SERGANT)

    2. После сброски настроек CE (удаления ветки реестра) при помощи ceregreset.exe, больше не предлагает запустить Tutorial.exe (будет предлагать если только вы запустили CE первый раз или на чистой системе)

    3. Исправлен баг в русской версии, при котором невозможно было выделить найденный адрес(SERGANT)

    4. Исправлен баг, который заемучил всех пользователей Vista/7 с темой Aero, а именно, при нажатии клавиши "Alt" исчезали компоненты (кнопки, текст, чекбоксы и т.д.). Данный баг не CE, а Delphi программ в целом.(SERGANT)

    5. Открытие окон создания трейнера при переходе по адресу в область памяти.

    6. Корректное удаление регистарции хот-кеев при их активации (наткнулся на этот баг когда звук нажатия хоткея повторялся после удаления этого хоткея)

  20. Статус: баг исправлен

    Корректное удаление регистрации хот-кеев при их активации (наткнулся на этот баг когда звук нажатия хоткея повторялся после удаления этого хоткея)

    Исправление:

    -модуль HotkeyHandler:

    -строка:251

    Было:

    setlength(hotkeythread.hotkeylist,length(hotkeythread.hotkeylist)-1);

    Изменил:

    setlength(hotkeythread.hotkeylist,length(hotkeythread.hotkeylist)-2);

    [sp=После первого удаления хоткея hotkeythread.hotkeylist был](((0, 0, 0, 0, 0), 5441632, 0, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 1, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 2, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 3, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 4, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 5, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 6, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 7, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 8, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 9, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 11, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 12, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 13, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 14, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 15, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 16, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 17, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 18, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 19, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 20, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 21, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 22, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 23, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 24, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 25, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 26, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 27, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 28, 0, 0, True, 0),

    ((0, 0, 0, 0, 0), 5441632, 29, 0, 0, True, 0),

    ((81, 0, 0, 0, 0), 5441632, 10, 0, 81, False, 0) // не очищалось поэтому надо скосить низ иначе при удалении хот-кея, он все равно остётся "живым" и информация о нём "наслаивается")[/sp]

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.