[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
Страница 1 из 11
Форум » Программирование и WEB-дизайн » C++ » Вопросы по IDA
Вопросы по IDA
SHooZ
Друзья
14.06.14 20:03
Offline
что это за код?

Код
.text:0070CFB0     _saveZBufferValues proc near            ; CODE XREF: sub_6FB630+35p
.text:0070CFB0                    ; sub_7002D0+79p
.text:0070CFB0                    ; sub_713950+5Dp
.text:0070CFB0                    ; sub_7154B0:loc_715B57p ...
.text:0070CFB0 000                 mov     eax, _RwEngineInstance
.text:0070CFB5 000                 fld     [eax+RwEngineInstance.dOpenDevice.zBufferNear]
.text:0070CFB8 000                 fstp    _savedZBufferNear
.text:0070CFBE 000                 fld     [eax+RwEngineInstance.dOpenDevice.zBufferFar]
.text:0070CFC1 000                 fstp    _savedZBufferFar
.text:0070CFC7 000                 retn
.text:0070CFC7     _saveZBufferValues endp

Если это функция, то как узнать список параметров?

BoPoH
Друзья
14.06.14 20:31
Offline
Нажми F5 (если у тебя, конечно, есть HexRays).

DK22Pac
Друзья
TOP Скриптер
18.06.14 15:01
Offline
Эта функция не принимает параметров.
Есть несколько способов определения кол-ва параметров.
Вл-первых, это фрейм переменных, который генерирует IDA.
Код
..text:007ED730     _VectorMultPoint proc near              ; DATA XREF: __rwVectorSetMultFn+8o
.text:007ED730                    ; __rwVectorOpen+12o
.text:007ED730
.text:007ED730     var_4           = dword ptr -4
.text:007ED730     arg_0           = dword ptr  4
.text:007ED730     arg_4           = dword ptr  8
.text:007ED730     arg_8           = dword ptr  0Ch
.text:007ED730

Переменные-аргументы имеют положительный оффсет - таких здесь 3 штуки.
Второе - это отслежение передачи параметров в функцию и зачистка стека после вызова функции.
Код
.text:0074AEF8 008                 push    eax
.text:0074AEF9 00C                 push    edi
.text:0074AEFA 010                 call    _RwLightStreamWrite
.text:0074AEFF 010                 add     esp, 8

Здесь передаются 2 параметра, и затем поправляется стек на 4*2 = 8 байт (одна переменная - 4 байта).
Стоит также определить тип декларации функции. Для thiscall первый параметр передается в регистре ecx, также thiscall и stdcall-функции сами чистят за собой стек.

Slivkin-Sergey
Модераторы
27.06.14 11:24
Offline
А можете на пальцах объяснить, что такое callback?

BoPoH
Друзья
27.06.14 12:57
Offline
Цитата Slivkin-Sergey ()
А можете на пальцах объяснить, что такое callback?

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

Представим, что нас есть система обработки анимаций (класс CAnimManager). В структуре класса анимации (которая создаётся и заполняется в ходе выполнения функции воспроизведения анимации) есть поле callback. К примеру, мы хотим воспроизвести одну анимацию, а следом за ней - другую. Объявление функции воспроизведения анимации будет выглядеть, грубо говоря и опуская лишние моменты, так:
Код
CAnimManager::PlayAnim(char *name, char *file, void *callback)

И вот у нас есть функции SetAnim1 и SetAnim2.
Код
void SetAnim2()
{
   CAnimManager::PlayAnim("anim2", "file1", 0);
}

void SetAnim1()
{
   CAnimManager::PlayAnim("anim1", "file1", &SetAnim2);
}

Где-то из игрового процесса мы вызовем функцию SetAnim1. Анимация с именем anim1 начнёт воспроизводиться. Когда обработчик анимаций дойдёт до конца анимации, он вызовет тот самый callback (функцию с определённым адресом), который мы указали в функции воспроизведения анимации. А та функция, в свою очередь, воспроизведёт следующую анимацию с именем anim2. Но в качестве параметра callback мы ставим 0, т.к. нам не нужно воспроизводить какую-либо анимацию ещё. Также вместо воспроизведения анимации anim2 мы могли вставить любое другое действие, которое необходимо выполнить по окончанию воспроизведения анимации anim1.

По сути, если говорить о callback, как о члене какой-либо структуры - это просто указатель на функцию. Но также мы можем назвать функцию колбэком (callback), если она предназначена для выполнения при условии, что произошло какое-то событие.
Отредактировал BoPoH - Пятница, 27.06.14, 12:57

Slivkin-Sergey
Модераторы
27.06.14 14:46
Offline
Ага... То есть если функция не вызывается ниоткуда, кроме одной специализированной функции, то она - коллбэк?..
Странное название, если честно.
А РендерКоллбэк, который упоминал листенер (который еще измеряет расстояние до камеры, чтобы прорисовывать лоды),вызывается примерно так?
Код
bool i = RenderCallback(texture1.x, texture1.y, texture1.z);
if(i == true)
// draw LOD

Довольно грубо описал, но суть, думаю, ясна.
[offtop]На опечатки внимания не обращай, пишу с планшета... Врагу не пожелаешь на нем печатать[/offtop]

BoPoH
Друзья
27.06.14 19:45
Offline
Цитата Slivkin-Sergey ()
То есть если функция не вызывается ниоткуда, кроме одной специализированной функции, то она - коллбэк?

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

Насчёт функции RenderCallback я не знаю. Вызов самого коллбэка обычно выглядит немного иначе. К примеру, если у тебя есть указатель Struct1 на структуру, в которой есть поле callback, то вызов будет выглядеть примерно так:
Код
Struct1->callback(param1, param2, param3);

Функция, из которой происходит вызов коллбэка, имеет непосредственный адрес в памяти функции коллбэка (т.е. указатель). Это нужно для того, чтобы подставить адрес нужного колбэка в нужном тебе случае.

Slivkin-Sergey
Модераторы
27.06.14 20:11
Offline
Цитата BoPoH ()
Функция, из которой происходит вызов коллбэка, имеет непосредственный адрес в памяти функции коллбэка (т.е. указатель). Это нужно для того, чтобы подставить адрес нужного колбэка в нужном тебе случае.

Слишком большая концентрация функций и коллбэков на квадратный пиксель моего монитора...
Цитата BoPoH ()
К примеру, если у тебя есть указатель Struct1 на структуру, в которой есть поле callback

В структуре можно хранить указатель на функцию?! Это как
Код
struct CCamera{
...
bool *RenderCallback();
}

? Или как-то по другому? И что тогда имел ввиду листенер, говоря, что "на каждый компонент можно навешивать свой коллбэк"?

И спасибо, что помогаешь чайнику освоиться, а не посылаешь на 7 букв - справка, которая у меня, к тому же, не открывается smile .

Добавлено (27.06.14, 19:09)
---------------------------------------------
Погодите-ка... А как же вызывается CPedPool.Initialise(), если пул - структура?

Добавлено (27.06.14, 19:11)
---------------------------------------------

Цитата BoPoH ()
Функция, из которой происходит вызов коллбэка, имеет непосредственный адрес в памяти функции коллбэка (т.е. указатель).

Вот эту часть я понял.
Цитата BoPoH ()
Это нужно для того, чтобы подставить адрес нужного колбэка в нужном тебе случае.

А вот эту, хоть убейте - нет. Как поможет указатель функции в коллбэке... Стоп... Чему он поможет?..

BoPoH
Друзья
27.06.14 22:48
Offline
Код
struct CCamera{
...
bool (*RenderCallBack)();
};

Я не знаю, как ещё понятней объяснить.
Возьму реальный пример. Я написал загрузчик для собственных плагинов (с расширениями fp и sp), в который встроил набор хуков для игры - на игровой процесс, на процесс камеры и т.д. Затем другие плагины (fp и sp) должны будут использовать эти же хуки для своих целей, но при этом они не будут ставить новые хуки в игру, а использовать те, которые сделал плагин-загрузчик. Выглядит часть инициализации примерно так:
Код
void RegisterHooks()
{
    redirectCall(0x53C0DF, &GameProcess);
    redirectCall(0x469376, &LoadGame);
}

Я объявил специальную структуру:
Код
struct Hook
{
    void (*CallBack)();
    int Type;
};

Далее объявил массив таких структур:
Код
Hook Hooks[1000];


Затем, написал такую функцию:
Код
void AddHook(int type, void (*CallBack)())
{
    for(int i = 0; i < 1000; i++)
    {
     if(Hooks[i].CallBack == 0)
     {
      Hooks[i].CallBack = CallBack;
      Hooks[i].Type = type;
      break;
     }
    }
}

Эта функция будет использоваться плагинами с расширением sp и fp. Она будет добавлять коллбэки из этих плагинов в массив структур типа Hook. (Функция экспортируется)

Рассмотрим функцию, которую мы использовали в качестве хука:
Код
void GameProcess()
{
    for(int i = 0; i < 1000; i++)
    {
     if(Hooks[i].CallBack > 0 && Hooks[i].Type == 1)
      Hooks[i].CallBack();
    }
    __asm mov  eax, 0x610BF0
    __asm call eax
}

Таким образом, когда в игре выполняется игровой процесс (ну, точнее, когда исполнение кода приходит в 0x53C0DF), выполняется функция GameProcess из нашего плагина-загрузчика. В свою очередь функция GameProcess пробегается по всему массиву и ищет структуры, где есть коллбэк и его тип равен 1 (для других хуков используются другие номера типов). Когда она находит коллбэк соответствующего типа, то вызывает его. И тогда выполняется функция, которую мы указывали при добавлении хука функцией AddHook из других плагинов.
Код
AddHook(1, &PluginGameProcess);
Отредактировал BoPoH - Пятница, 27.06.14, 22:50

DK22Pac
Друзья
TOP Скриптер
28.06.14 02:34
Offline
Цитата Slivkin-Sergey ()
А можете на пальцах объяснить, что такое callback?

Это функция, которая вызывается через указатель (например, функция-параметр для другой функции).
Код
RwFrame *RwFrameForAllChildren(RwFrame *frame, int (__cdecl *callback)(DWORD, DWORD), void *data)
{
      RwFrame *child = frame->child;
      if ( child )
      {
        do
        {
          if ( !callback(child, data) )
            break;
          child = child->next;
        }
        while ( child );
      }
      return frame;
}

Здесь мы в функцию передаем адрес другой функции, которую вызываем для каждого дочернего фрейма для фрейма frame.
Рендер-коллбэк вызывается при рендере атомиков, указатель на функцию рендера хранится в структуре атомика.
(
Цитата Slivkin-Sergey ()
Погодите-ка... А как же вызывается CPedPool.Initialise(), если пул - структура?

Структура (если не смотреть очень глубоко) ничем не отличается от класса, кроме того, что в классе члены по умолчанию имеют private-доступ.
Отредактировал DK22Pac - Суббота, 28.06.14, 04:51

SHooZ
Друзья
22.08.14 02:26
Offline
Несколько вопросов.

1) Вызов функций. По мануалу listener'a функция по адресу 0x47D5C9 - stdcall, потому-что  в ней return с параметрами. Является ли такое ее объявление корректным? И где можно получить больше примеров определения типов функций?

Код
void ObjectPutAt(CEntity *object, float x, float y, float z)
{
      ((void (__stdcall *)(CEntity *, float, float, float))0x47D5C9)(object, x, y, z);
}

  
2) Откуда при декомпиляции в IDA брать оригинальные названия классов? Я пробовал декомпилировать ехе'шники GTA VS и GTA III, но там названий классов и структур не сохраниось, тогда декомпилировал одну из своих программ - результат тот же.

BoPoH
Друзья
22.08.14 19:54
Offline
Цитата SHooZ ()
Является ли такое ее объявление корректным?
Вполне. Все остальные типы (__cdecl, __fastcall и __thiscall) объявляются таким же образом. Естественно, "__stdcall" нужно заменить на соответствующий тип функции. Чтобы узнать тип функции, достаточно нажать F5 в IDA, чтобы плагин HexRays преобразовал указанную функцию в код на C++. Там ты сможешь увидеть тип функции.
Цитата SHooZ ()
Откуда при декомпиляции в IDA брать оригинальные названия классов?
Ну, раньше их просто придумывали сами. Некоторая информация просочилась, поэтому можно было дать некоторым классам их настоящие названия. Сейчас же вышла мобильная версия GTA SA, где сохранились названия всех функций. Благодаря этому можно узнать оригинальные названия классов и функций. Достаточно открыть .apk игры в IDA и дождаться окончания автоанализа.
Отредактировал BoPoH - Пятница, 22.08.14, 19:56

SHooZ
Друзья
22.08.14 20:22
Offline
BoPoH, эта функция вызывается опкодом, при преобразовании в С++ появляется куча мусора. Можешь сам на неё глянуть?

BoPoH
Друзья
22.08.14 23:42
Offline
Я даже не подумал проверить, куда ведёт адрес, указанный в вызове функции. Так делать нельзя. Опкоды - это одно. Функции - это другое. Это не функция, а часть функции, в которой происходит обработка опкодов. Проще всего задать объекту позицию через его матрицу.

Код
CEntity *object = CObjectCreate(666); // предположим, тут мы создали объект
object->placeable.m_pCoords->matrix.pos.x = 0.0;
object->placeable.m_pCoords->matrix.pos.y = 0.0;
object->placeable.m_pCoords->matrix.pos.z = 0.0;
Вызвать опкод через C++ нельзя, ведь опкод обрабатывается специальной функцией, которая собирает все данные через структуру скрипта. В теории, можно написать эмулятор вызова опкодов, но в этом мало смысла.
Отредактировал BoPoH - Пятница, 22.08.14, 23:44

DK22Pac
Друзья
TOP Скриптер
23.08.14 05:01
Offline
Hex-rays не всегда определяет тип функции, типы и кол-во параметров верно.
Например, thiscall, в котором нет обращения к this, может быть представлен как stdcall.
thiscall без параметров может быть представлен как fastcall.
Отредактировал DK22Pac - Суббота, 23.08.14, 05:03

BoPoH
Друзья
23.08.14 14:13
Offline
Цитата DK22Pac ()
Hex-rays не всегда определяет тип функции, типы и кол-во параметров верно. Например, thiscall, в котором нет обращения к this, может быть представлен как stdcall.
thiscall без параметров может быть представлен как fastcall.
Ну, для новичка такой способ вполне подойдёт.

Форум » Программирование и WEB-дизайн » C++ » Вопросы по IDA
Страница 1 из 11
Поиск: