Объявления

Друзья, если не получается зарегистрироваться, напишите на почту vdv_forever@bk.ru.
Я оторву свою задницу от всех дел и обязательно Вас активирую! :smile10:
Добро пожаловать на геройский форум! :smile25:

База данных IDA от void17

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineАватара пользователя
void_17  
имя: DM
Ветеран
Ветеран
 
Сообщения: 537
Зарегистрирован: 25 апр 2021, 15:05
Откуда: Оттуда
Пол: Мужчина
Поблагодарили: 122 раз.

Re: База данных IDA от void17

Сообщение void_17 » 03 апр 2024, 03:38

Там всё правильно, просто компилятор выделил память для другой переменной на стеке в том же месте, где середина структуры, т.к. дальше в коде она не используется.
Чуть ниже это видно наглядно:
Код: Выделить всё
awake_enemy_value = _estimate.awake_enemy_value;
this->iNextAction = 6;
this->iNextActionExtra = awake_enemy_value; // это не имеет смысла, очевидно другая переменная на том же месте в стеке
this->iNextActionGridIndex = v42->gridIndex;
return 1;


Посмотрел декомпилят в других базах, так и есть.
Для разделения переменных есть force new variable, но я не знаю как разделить переменные структуры, выделенной на стеке.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5547
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2162 раз.

Re: База данных IDA от void17

Сообщение AlexSpl » 03 апр 2024, 12:09

Если сделать undefine (u) этой функции и заново проанализировать (p), то получается более читаемый код. Например, вместо этого:

Код: Выделить всё
if ( v57.best_value <= 0
  || (current_army->sMonInfo.attributes & CF_SUMMONED) != 0
  || !gpGame->sSetup.difficulty && !*(_BYTE *)(_estimate.our_group + _estimate.enemy_group + 21668) )
{
    goto LABEL_71;
}

это:

Код: Выделить всё
if ( v57.best_value <= 0
  || (current_army->sMonInfo.attributes & 0x400000) != 0
  || !gpGame->sSetup.difficulty && !v68->IsHuman[current_group] )
{
    goto LABEL_71;
}

И вместо этого:

Код: Выделить всё
_estimate.awake_enemy_value = v57.best_hex;
_estimate.enemy_combat_value = v57.best_attack_time;
_estimate.friendly_combat_value = v30 * v57.best_value / (100 * v57.best_attack_time);
_estimate.awake_friendly_value = v31 / v57.best_attack_time;

это:

Код: Выделить всё
target_hex = v57.best_hex;
best_attack_time = v57.best_attack_time;
v62 = v30 * v57.best_value / (100 * v57.best_attack_time);
v64 = v31 / v57.best_attack_time;

Если объявить _estimate, то код в самом начале функции сильно страдает. С _estimate получается слишком нечитаемо, а без - почти красота:

Код: Выделить всё
if ( gpGame->iGameType >= 2 )
    combatManager::SoD_mark_moat(this, current_army, enemy_attacks, &v5->lowest_attack);
if ( gpGame->sSetup.difficulty > 0 || this->IsHuman[current_group] )
    combatManager::mark_enemy_attacks(this, current_army, enemy_attacks, &dangerous_enemies, v5);
if ( gpGame->sSetup.difficulty >= 2 || this->IsHuman[current_group] )
    combatManager::mark_friendly_armies(this, current_army, enemy_attacks, dangerous_enemies, v5);
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5547
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2162 раз.

Re: База данных IDA от void17

Сообщение AlexSpl » 03 апр 2024, 15:13

Было бы здорово изменять тип переменной с помощью force new variable по ходу листинга. Вот здесь область стека используется, как несколько независимых переменных, а вот тут - как поля структуры. Но, по моему опыту, рано или поздно получается каша, ибо тип переменных в листинге до переопределения меняется (не всегда, но я не понял, когда именно) после переопределения. А нужно, чтобы ты самостоятельно мог указывать, на каких отрезках листинга какой тип имеют данные, расположенные по одному и тому же адресу в стеке. Конкретно в рассматриваемой выше функции _estimate (как локальная переменная) определяется ближе к концу листинга, и я подозреваю, что и в исходном коде присутствует её объявление непосредственно перед использующим её кодом. В IDA же все определения типов задаются в самом начале функции (Pascal-style), что неудобно. Огромным плюсом была бы возможность прописывать определения типов переменных вручную прямо в листинге, чтобы весь код ниже брал новое определение типа переменной, а не старое.

Если бы компилятор оптимизировал код по хардкору, то получался бы вообще нечитаемый декомпилят. Например, есть 4 байта на стеке, которые код использует как указатель на совершенно разные переменные/экземпляры классов. Вот здесь блок кода отработал с указателем на army, следующий блок работает с этими 4-мя байтами, как с указателем char**, а код после - как с указателем combatManager*. И все указатели расположены по одному и тому же адресу :smile1:

* * *
Я тут подумал, что компилятор не вправе занимать место одной переменной на стеке другой переменной, если они находятся в одной и той же области видимости (scope), но может разместить новую переменную по адресу старой, которая больше не актуальна (пережила свой scope).

Код: Выделить всё
int a;
{
    int b;
}

b не может занять адрес a, ибо a потенциально может использоваться далее по коду.

А вот в случае наоборот a может быть размещена по адресу b:

Код: Выделить всё
{
    int b;
}
int a;

Получается, _estimate занимает место на стеке тех переменных, чей scope уже закончился. Надо будет поиграть с force new variable, не может быть, чтобы был такой хаос, всё-таки IDA - коммерческий продукт. И 1% нюансов декомпиляции не знаю, но было бы круто, если бы переменные объявлялись в листинге непосредственно перед первым блоком кода, который их использует.
Вернуться к началу

offlineАватара пользователя
void_17  
имя: DM
Ветеран
Ветеран
 
Сообщения: 537
Зарегистрирован: 25 апр 2021, 15:05
Откуда: Оттуда
Пол: Мужчина
Поблагодарили: 122 раз.

Re: База данных IDA от void17

Сообщение void_17 » 04 апр 2024, 06:53

Цитата:
Вот здесь область стека используется, как несколько независимых переменных, а вот тут - как поля структуры.


Я про это и говорю. К сожалению действительно нет разделения переменных по ходу функции.
Все что приходится — структуры переопределять в бездушные буфферы из байтов.

Есть еще один вариант — если прям очень хочется декомпилировать функцию с высокой точностью, можно определить union, и по мере использования стека выбирать Union Member
, однако это засоряет библиотеку типов. У меня и так уже в базе 1000(!) вручную набранных типов накопилось(скоро, кстати, обновлю базу, релиз NH3API тоже не за горами).
Хотя, наверное, можно сделать резервную копию базы, наплодить union-ов, декомпилировать функцию и не сохранять такую базу. Однако это костыли, хотелось бы управлять стеком по ходу функции, но увы, исходный код декомпилятора HexRays закрыт и всех подробностей декомпиляции не узнать. Впрочем, есть IDA SDK, я даже плагины какие-то писал для ускорения разработки NH3API. Там в т.ч. есть SDK декомпилятора, надо вкурить, может все-таки можно разделять стек. Но пока что на это у меня времени нет.
Цитата:
Надо будет поиграть с force new variable, не может быть, чтобы был такой хаос, всё-таки IDA - коммерческий продукт.


В этом и проблема, issue не откроешь как говорится. :smile1:
Можно, кстати, попробовать декомпилятор ghidra (есть плагин для IDA Pro), но он очень сырой даже для x86...
Вернуться к началу

offlineАватара пользователя
void_17  
имя: DM
Ветеран
Ветеран
 
Сообщения: 537
Зарегистрирован: 25 апр 2021, 15:05
Откуда: Оттуда
Пол: Мужчина
Поблагодарили: 122 раз.

Re: База данных IDA от void17

Сообщение void_17 » 04 апр 2024, 07:02

Я думаю, такой способ выглядит примерно так:
(в этом условном примере в условной функции sub_404040 локальная float переменная лежит в стеке там же, где предпоследнее поле std::string)
Код: Выделить всё
#pragma pack(push, 1)
struct ActualStruct
{
int member1;
int member2;
void* member3;
std::string member4;
};

struct sub_404040_DummyStack_t
{
byte dummy1[sizeof(ActualStruct)-8];
float functionLocalVar;
byte dummy2[4];
};
#pragma pack(pop)

union sub_404040_StackUnion_t
{
ActualStruct structObject;
sub_404040_DummyStack_t dummy;
};


Вместо ActualStruct переменная будет иметь тип sub_404040_StackUnion_t, и по ходу функции можно выбирать Union Member.

Вообще я только что сейчас его на коленке придумал, комп далеко, проверьте.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5547
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2162 раз.

Re: База данных IDA от void17

Сообщение AlexSpl » 04 апр 2024, 14:47

Я ещё, когда только начинали помогать с разбором функций (в первых версиях базы), использовал union для выбора правильного типа переменных в тех случаях, когда они пересекаются выделенным адресным пространством (см. здесь), но потом, помню, отказался, ибо, как Вы правильно заметили выше, база засоряется мусорными типами, т.к. комбинаций типов полей union'а может быть очень много.
Вернуться к началу

offlineАватара пользователя
void_17  
имя: DM
Ветеран
Ветеран
 
Сообщения: 537
Зарегистрирован: 25 апр 2021, 15:05
Откуда: Оттуда
Пол: Мужчина
Поблагодарили: 122 раз.

Re: База данных IDA от void17

Сообщение void_17 » 04 апр 2024, 18:31

Значит надо сделать плагин для IDA Pro, который будет генерировать тип, который не будет отображаться в библиотеке типов(такой функционал есть).
Вернуться к началу

offlineАватара пользователя
void_17  
имя: DM
Ветеран
Ветеран
 
Сообщения: 537
Зарегистрирован: 25 апр 2021, 15:05
Откуда: Оттуда
Пол: Мужчина
Поблагодарили: 122 раз.

Re: База данных IDA от void17

Сообщение void_17 » 18 апр 2024, 05:55

Работа над NH3API продолжается.
1) Реализовал больше половины <type_traits>, которые работают аж в Visual Studio 2008 (!)
Что-то подсмотрел из boost, а что-то изобретательно написал самостоятельно, например std::underlying_type, и оно работает как часы! :smile20:

 
Код: Выделить всё
template<typename EnumType, typename IntType>
struct enum_same_type
{
    static const bool value = (sizeof(EnumType) == sizeof(IntType))
                              && (std::is_convertible<EnumType, IntType>::value)
                              && (std::is_unsigned<EnumType>::value == std::is_unsigned<IntType>::value)
                              && (std::is_signed<EnumType>::value == std::is_signed<IntType>::value);
};

#define NH3API_ENUM_ST(TYPE) enum_same_type<EnumType, TYPE>::value NH3API_MACRO_COMMA TYPE

template<typename EnumType, bool IsEnum>
struct enum_underlying_impl // false, false case
{
    typedef void type;
};

template<typename EnumType>
struct enum_underlying_impl<EnumType, true> // this is UGLY and certainly will slow down the compile time.
                                  // but this will make std::underlying_type work in VS2005...
{
    typedef typename
         std::conditional<NH3API_ENUM_ST(signed __int8), typename
            std::conditional<NH3API_ENUM_ST(unsigned __int8), typename
            std::conditional<NH3API_ENUM_ST(signed __int16), typename
            std::conditional<NH3API_ENUM_ST(unsigned __int16), typename
            std::conditional<NH3API_ENUM_ST(signed __int32), typename
            std::conditional<NH3API_ENUM_ST(unsigned __int32), typename
            std::conditional<NH3API_ENUM_ST(signed __int64), typename
            std::conditional<NH3API_ENUM_ST(unsigned __int64), void>::type >::type >::type >::type >::type >::type >::type >::type type; // ::type MACHINE GUN!!!
};
#undef NH3API_ENUM_ST

#define NH3API_CORRESP_STYPE_SPEC(TYPE) template<> struct corresp_signed_type<unsigned TYPE> { typedef signed TYPE type; };
#define NH3API_CORRESP_UTYPE_SPEC(TYPE) template<> struct corresp_signed_type<signed TYPE> { typedef unsigned TYPE type; };
#define NH3API_CORRESP_SPEC(TYPE) NH3API_CORRESP_STYPE_SPEC(TYPE) NH3API_CORRESP_UTYPE_SPEC(TYPE)

template<typename T>
struct corresp_signed_type
{ typedef void type; };

template<typename T>
struct corresp_unsigned_type
{ typedef void type; };

NH3API_CORRESP_SPEC(__int8)
NH3API_CORRESP_SPEC(__int16)
NH3API_CORRESP_SPEC(__int32)
NH3API_CORRESP_SPEC(__int64)

template<typename T>
struct make_signed
{
    typedef typename std::remove_cv<T>::type no_cv_t;
    // 1) if T is enum, convert to signed underlying type.
    // 2) also copy cv qualifiers
    typedef typename __nh3api::copy_cv<typename std::conditional<std::is_enum<T>::value,
                              typename enum_underlying_impl<no_cv_t, true>::type,
                              typename corresp_signed_type<no_cv_t>::type>::type, T>::type type;
};

template<typename T>
struct make_unsigned
{
    typedef typename std::remove_cv<T>::type no_cv_t;
    // 1) if T is enum, convert to signed underlying type.
    // 2) also copy cv qualifiers
    typedef typename __nh3api::copy_cv<typename std::conditional<std::is_enum<T>::value,
                              typename enum_underlying_impl<no_cv_t, true>::type,
                              typename corresp_unsigned_type<no_cv_t>::type>::type, T>::type type;
};

#define NH3API_MAKE_SIGNED_SPEC(TYPE) template <> struct make_signed<TYPE> { typedef signed TYPE type; };
#define NH3API_MAKE_UNSIGNED_SPEC(TYPE) template <> struct make_unsigned<TYPE> { typedef unsigned TYPE type; };

NH3API_MAKE_SIGNED_SPEC(char)
NH3API_MAKE_SIGNED_SPEC(const char)
NH3API_MAKE_SIGNED_SPEC(volatile char)
NH3API_MAKE_SIGNED_SPEC(const volatile char)

NH3API_MAKE_UNSIGNED_SPEC(char)
NH3API_MAKE_UNSIGNED_SPEC(const char)
NH3API_MAKE_UNSIGNED_SPEC(volatile char)
NH3API_MAKE_UNSIGNED_SPEC(const volatile char)

#undef NH3API_MAKE_SIGNED_SPEC
#undef NH3API_MAKE_UNSIGNED_SPEC


} // namespace impl
} // namespace __nh3api

namespace std
{

template <class T> struct is_signed : public integral_constant<bool, __nh3api::impl::is_signed_impl<T>::value> {};
template <class T> struct is_unsigned : public integral_constant<bool, __nh3api::impl::is_unsigned_impl<T>::value> {};

template <class T>
struct make_signed
{
    // if it is already signed, leave T unchanged.
    typedef typename std::conditional<std::is_signed<T>::value,
                             T,
                             typename __nh3api::impl::make_signed<T>::type>::type type;
};

template<class T>
struct underlying_type
{
private:
   static const bool is_enum = std::is_enum<T>::value;
public:
   typedef typename __nh3api::copy_cv<typename __nh3api::impl::enum_underlying_impl<T, is_enum>::type, T>::type type;
};

}

Я наверное даже это дело впихну в header-only библиотеку и запилю репозиторий на GitHub, чтобы функционалом type_traits могли пользоваться консерваторы на Visual Studio 2008, которые не имеют дела с NH3API :smile17:

2) Контейнеры в NH3API будут работать не непосредственно через std::allocator, но и через std::allocator_traits и std::pointer_traits, которые тоже успешно реализованы. Всё это нужно для максимальной совместимости .exe-шных контейнеров с STL от C++98 и вплоть до C++23.

3) Так же, для пользователей Visual Studio будут доступны файлы .natvis, которые позволяют удобно просматривать структуры NH3API во время дебага.
Возможно я еще сделаю pretty-принтеры для GDB.

4) К сожалению, библиотеку полностью хедер-онли сделать не получится. :smile29: По крайней мере в Visual Studio линкер не может глобальные переменные найти. Так что готовьте CMake, такие дела.
Вернуться к началу

Пред.

Вернуться в Общий раздел

Кто сейчас на конференции

Сейчас этот форум просматривают: Yandex [bot] и гости: 2