You are not logged in.
ACFF1F28
Нашел я такой. (Вообще, по инету ходит два exe-шника: 1.0 US и 1.0 EU. Указанная сумма - от US. Я измывался над EU).
Тихонько начал перетаскивать расписанное на US-овский.
Но, перед тем, как начинать расписывать, хочется сделать следующее:
было:
.text:00401A1D loc_401A1D: ; CODE XREF: sub_43B0F0:loc_43B99Bj .text:00401A1D push ecx .text:00401A1E pushf .text:00401A1F stc .text:00401A20 mov ecx, ds:dword_15606A8 .text:00401A26 adc ecx, ds:dword_65FB62 .text:00401A2C popf .text:00401A2D xchg ecx, [esp] .text:00401A30 jmp loc_43B9A0
Курсор ставится на MOV, в меню idc выбирается написанный ночью скрипт. Становится:
.text:00401A1D loc_401A1D: ; CODE XREF: sub_43B0F0:loc_43B99Bj .text:00401A1D push ecx .text:00401A1E pushf .text:00401A1F mov ecx, offset dword_9691C0 .text:00401A24 nop .text:00401A25 nop .text:00401A26 nop .text:00401A27 nop .text:00401A28 nop .text:00401A29 nop .text:00401A2A nop .text:00401A2B nop .text:00401A2C popf .text:00401A2D xchg ecx, [esp+0] .text:00401A30 jmp loc_43B9A0
После первых 30 правок - вроде запускается и работает.
Вторым этапом будет скрипт (пока не написанный), приводящий это все к совсем нормальному виду:
push offset dword_9691C0 jmp loc_43B9A0
Третьим этапом - возврат команды на место (в том архиве, что я закидывал - move.idc)
И последним этапом - возврат на место функций из сегмента HOODLUM. (но, перед этим, из них практически вручную придется выкинуть немного мусора).
Если кому интересны скрипты - могу куда-нибудь выкладывать по мере написания.
В рузультате должен получиться .exe в 5.5M, в который будет нормально читаться без помощи калькулятора.
Offline
2listener:
нам, пожалуй, будет интересен конечный результат повторить подобный подвиг я не берусь
Offline
2Seemann:
Можешь написать параметры функций (из первого поста) и значения кототрые они возвращают (хотя бы некоторых).
Last edited by Sanchez (03-02-2007 09:08)
Offline
2Sanchez:
даже не знаю, как такое сделать на автомате... вручную я запарюсь копировать параметры. Ты лучше скажи какие тебя интересуют.
Offline
даже не знаю, как такое сделать на автомате... вручную я запарюсь копировать параметры. Ты лучше скажи какие тебя интересуют.
Ладно, тогда не надо, я думал они у тебя в готовом виде есть.
Offline
2listener:
Поскольку ты видел, что называется, "вживую" exe US и EU версии, скажи в чем между ними разница. Какие адреса могут различаться? Ты вроде говорил, что все те адреса, которые я постил, которые есть на гтамоддинге, у тебя совпадали, разница только в графической составляющей?
Offline
2Seemann:
В EU, практически сразу перед winmain, есть кусок, который определяет установленный язык системы и, в зависимости от этого, выбирает .gxt
Плюс, что-то вырезано в немецкой версии (кажется, от убитых не остается денег). (В моей базе это немного расписано, копать от функции _IsGermanGame)
Графические либы - одинаковые (Renderware Graphics 3.6)
Offline
Тот который из 2х (англ, исп.) это US версия. Который из 4-х (франц., итал., исп., англ.) - это EUR.
Это мы уже выясняли
Offline
Могу кинуть базу. (IDA 5.0.0.879). Там все, конечно, в жутком состоянии, но что-то полезное из нее можно получить.
Выложи, пожалуйста, очень интересно посмотреть.
[large][acronym=Завтра, завтра, постоянно завтра, так проходит жизнь]Cras, cras, semper cras, sic evadit aetas[/acronym][/large]
Offline
2AleX AciD:
[You must login to view hidden text.]
надеюсь, listener не обидится
Offline
надеюсь, listener не обидится
Буду только рад.
Надеюсь, как чутка развяжусь с текущими делами, выложить новую базу (по компактному exe-шнику, плюс там будет расписана большая часть конструкторов и деструкторов).
Offline
2listener:
Есть идеи насчет функций 008286E0 и 0082871B? Я заметил, они находятся в начале и конце всех функций, работающих с выделением памяти.
Offline
2listener:
Есть идеи насчет функций 008286E0 и 0082871B? Я заметил, они находятся в начале и конце всех функций, работающих с выделением памяти.
Идей нет. Есть точная информация. Это выделение/освобождение exception frame. Первыя называется __try, как точно называется вторая, честно говоря, не помню. Исходники стандартных библиотек я взял из MSVC. Практически все используемые функции libc у меня в базе расписаны.
Вообще, вся работа с динамической памятью делается либо (С++ style)
.text:0082119A ??2@YAPAXI@Z (void * __cdecl operator new(unsigned int)) .text:008214BD ??3@YAXPAX@Z (void __cdecl operator delete(void *))
либо (C style)
.text:00824257 void *__cdecl malloc(size_t) .text:0082413F void __cdecl free(void *)
либо, внутри библиотек RW, через RwEngineInstance.memoryFuncs
Копаться же в потрохах memory manager-а - занятие неблагодарное и, в общем, бесполезное
Про обработку исключений в MSVC хорошо написано здесь: http://www.openrce.org/articles/full_view/21
PS. Сейчас, перетащу остатки инфы из старой базы в новую и выложу. Там будет расписана не только большая часть libc, но и практически все функции RW, вызываемые из основного кода (с типами и структурами).
PPS. Только что обнаружил, что либо CPed и CBuilding наследуются от CObject, либо все три наследуются от общего предка.
Offline
Как-то тут тихо стало. Непорядок.
Я, за последние два дня разобрался с кое-какими С++-ными фишками. В итоге, по списку функций есть некоторый прогресс.
http://www.megarulez.ru/tmp/sa_funcs_20070416.txt (это только то, что есть у меня в новой базе. Из старой туда перенесено далеко не все)
Условные обозначения:
_sub - функция не используется (не используемый блок кода (например, сетевой код), ушла в инлайн или что-то в этом роде)
dummy - функция делающая что-то тривиальное (от одного до четрыех операторов, одна строка). Согласен, название не очень хорошее, из-за возможной путаницы с объектом CDummy. Потом я их как-то переименую.
__scalar_constructor/__scalar_destructor - вставленные компилятором вставки для создания объекта. Что-то такое и зачем нужно - тема отдельного рассказа, важно учитывать, что этот код полностью автоматически сгенерированный и его можно не анализировать. Аналогично, с векторными версиями.
_Object__method - чтобы не возиться с манглингом имен, в такой форме записываются названия методов объекта, т.е. Object::method
_Object1_Object2__method - аналогично предыдущему варианту, шаблоные классы Object1<Object2>::method
_NNNNN_method - название объекта пока непонятно, вместо него используется адрес VMT (опять-таки, тема отдельного рассказа)
_fNN - поле структуры/объекта по смещению NN в структруе объекта.
_mNN - виртуальный метод, расположенный по смещению NN в VMT.
_aNN - аргумент функции по смещению NN на стэке (типично, _a0 - первый аргумент, _a4 - второй и т.д.).
Last edited by listener (16-04-2007 19:25)
Offline
Зачем нужно копаться в совсем "левых" вещах? Все просто: чем больше есть информации по структуре программы, тем проще разбираться, что делают неизвестные куски.
Насколько я знаю, разбором структры объектов никто не занимался. На gtamodding делались попытки расписать структры CPed, СVehicle и т.д.
Жестокая правда заключается в том, что таких структур нет.
Есть единая иерархия объектов игры, включающая в себя несколько десятков объектов. Например, для каждого из 12 типов транспортных средств есть свой класс со своей структурой (в которой примерно половина полей уникальная для данного класса). Все они наследуются от CVehicleBase, который унаследован от еще какого-то класса, который, в свою очередь наследуется от какого-то CObject (какого-то, потому что у CObject есть своя иерархия).
Аналогично, есть несколько классов для ped-ов (унаследованных от CObject), несколько building-ов и т.д.
Отдельная вещь (упоминаний о которой я тоже нигде не встречал) - объекты, отвечающие за базовое поведение (не контролируемое скриптом) и обработку событий. Первым занимается группа объектов, унаследованных от CTaskBase, вторым - потомки CEventBase (CEvent_85AB38, если я ничего не путаю).
Это было (сумбурное) описание текущего состояния. Теперь у меня есть несколько вопросов:
1) нужно ли подробное описание, как устроена структура объектов в памяти/коде или все и так это знают?
2) можно ли где-нибудь найти описание кодов CTask, иначе чем из команд assign behavior TaskID to object ?
Offline
1) нужно ли подробное описание, как устроена структура объектов в памяти/коде или все и так это знают?
нужно. интересно, а какой объект самый "первый", от кого все наследуется?
кстати, а эти объекты суть творение R* или исходит от RW?
2) можно ли где-нибудь найти описание кодов CTask, иначе чем из команд assign behavior TaskID to object ?
я не встречал. было бы тоже интересно узнать. AFAIK, они используются в опкодах:
062E: unknown_get_actor 2@ task 1560 status_store_to 17@ // ret 7 if not found
Offline
Уф. Сдампил кусочек /dev/brain Возможно стоит нарисовать к этому еще несколько картинок.
К вопросу об анализе объектно-ориентированного кода
Одним из основных плюсов ООП, применительно к традиционныи языкам (C->C++, Pascal->Object Pascal/Delphi и т.д.), является возможность оставлять "за кадром" часть рутинных операций, изрядно облегчая жизнь программисту.
При анализе ОО-кода, все то, что было сгенерированно компилятором, доступно и очевидно. Если не предпринимать меры, и не выкидывать предварительно автоматически созданный код, в процессе анализа, приходится учитывать дополнительные килобайты кода и десятки функций, которые не несут явной смысловой нагрузки (т.е., не относятся напрямую к логике программы). Напротив, отдельный разбор и выделение компиляторных вставок позволяет без особого труда выделить часть структур и кода.
В предельном упрощении, объект можно представить в виде структуры (содержащей состояние объекта) и набора функций, которые это состояние меняют. Чем же в реальной жизни объект отличается от комбинации "структура и набор функций"?
Каждый нестатический метод, получает дополнительный параметр this - указатель на объект, для которого вызывается этот метод.
MSVC передает этот параметр не на стэке, а в регистре ECX. Т.о., определить, что рассматриваемая функция является методом, очень просто: если в ней используется ECX без предварительного определения, то это не просто функция, а метод.
(типично, ECX сохраняется в каком-то другом регистре, например MOV ESI, ECX)
Виртуальные методы
Важным свойством объектов является полиморфизм. Т.е., грубо говоря, метод с одним и тем же именем, для объектов разных классов
ведет себя по разному. В коде это реализуется с помощью структуры, называемой "таблицей виртуальных методов" (VMT).
Если у объекта есть хотя бы один виртуальный метод, в объект добавляется дополнительное поле (всегда первое, по нулевому
смещению), содержащее указатель на VMT. Сам VMT содержит массив указателей на виртуальные методы объекта (обычно, в том
порядке, в котором они встречаются в исходнике). Порядок функций не меняется в потомках объекта, новые методы добавляются в
конец VMT. (Т.е., если есть класс A с виртуальными методами a, b и с, а в его потомке B определяются виртуальные методы b и d,
то B::__vmt__ будет содержать указатели на a, b, c, d, при этом b будет указывать на переопределенный метод).
Почему так важен порядок в VMT? Основным идентификатором виртуального метода является смещение в VMT. Т.е., вызов виртуального
метода выглядит таким образом (предполагается, что в ECX находится this):
mov eax, [ecx] mov eax, [eax+ObjectVMT.id] call eax
Еще, стоит заметить, что в двоичном коде, адрес VMT однозначно соответствет классу, и, на промежуточных этапах, когда имя класса определить еще не возможно, может использоваться как идентификатор этого класса.
Наследование
При наследовании, объект-потомок сохраняет всю структуру полей объекта-предка, добавляя к ней набор своих полей.
Инициализация и освобождение объектов
Для того, чтобы проинициализировать объект и освободить полученные им ресурсы, используются специальные методы - конструктор и деструктор соответственно. Конструкторов у объекта может быть несколько, деструктор всегда один.
Отличие конструктора и деструктора от обычных методов в том, что кроме явно прописанных в тексте программы действий, требуется еще и проинициализировать VMT.
Важно отметить, что VMT инициализируется как в конструкторе, так и в деструкторе. Это требуется для того, чтобы корректно обрабатывалась цепочка вызовов для объектов потомков (в конструкторе) и объектов-предков (в деструкторе).
С точки зрения reverse engineering-а, наличие такого механизма позволяет отслеживать дерево наследования объектов.
(Т.е., если внутри конструктора вызывается функция, инициализирующая VMT объекта, а затем VMT переинициализируется - вызванная функция - конструктор объекта-предка).
Offline
Выделение памяти
Мало проинициализировать поля объекта, под него требуется еще и выделить память.
Для объекта, как и для любой переменной, существует три способа выделения памяти: глобальный (в сегменте данных), локальный
(на стэке) и динамический (в куче). Для всех трех способов используются разные механизмы инициализации.
Конструктор объекта выполняет только инициализации самого объекта, не касаясь того, как для него выделена память.
это нужно для унификации - чтобы, в большинстве случаев, ограничиться одним конструктором (и одним деструктором)
для всех случаев. Весь остальной код по созданию и освобождению объекта генерируется динамически.
Проще всего, выделить объект глобально. На этапе линковки для него резервируется память, а в список инициализации, используемый
функцией _cinit, добавляется вызов конструктора объекта для этого зарезервированного блока.
При локальном выделении, резервируется область на стэке. При входе в стэковый фрэйм, в котором используется объект, для
этой области стэка, вызывается конструктор, а при выходе - деструктор. Т.е., если в исходнике есть код:
{
Object o (params);
... какой-то код ...
}
на выходе, мы получим:
{
Object * o = alloca (sizeof (Object));
o->Object (params);
... какой-то код ...
o->~Object ();
}
Сложнее всего, с динамическим выделением. Для каждого объекта, компилятор создает, специальную функцию - скалярный деструктор,
который вызывает "настоящий" деструктор объекта и, если попросили, освобождает память. Типичный скалярный деструктор выглядит
так:
void Object::scalar_destructor (bool release) {
this->~Object ();
if (release)
free (this);
}
Скалярные конструкторы, как правило, не выделяются в отдельные функции, а генерируются "по месту".
Кроме скалярных, есть и векторные конструкторы и деструкторы. Они предназначены для инициализации и освобождения массивов объектов.
Т.е., если требуется выделить массив объектов, в задачи скалярного конструктора входит вызов конструктора объекта для каждого элемента массива.
Offline
1) нужно ли подробное описание, как устроена структура объектов в памяти/коде или все и так это знают?
нужно. интересно, а какой объект самый "первый", от кого все наследуется?
кстати, а эти объекты суть творение R* или исходит от RW?
Уф. Написал немного. Продолжать стоит? Возможно, чтоит проиллюстрировать какми-то кусочками реального кода? (Если да, то какими).
Что касается структуры объектов в gta_sa.exe, единой иерархии там нет.
Есть иерархия об]ектов, содержащих какие-либо 3D элементы (CObjectBase/CDummyBase/CBuildingBase/CPedBase/CVehicleBase, со всеми их потомками; ).
Есть иерархии объектов AI/поведения (CTask с потомками, CEvent с потомками, CPedAttractor, СPedGroup и, возможно что-то еще).
Обработчики конфигов - отдельные объекты на каждый конфиг (и то не на всякий), не включенные в какую-либо иерархию. Аналогично, CScript, CIplFile и т.д.
В общей сложности, 200-300 классов. Я сейчас (по мере очередного прохода по базе) описываю иерархии. Как допишу - выложу.
RW Graphics написана практически полностью на чистом C (т.е., объектов там очень мало, как правило - это сторонние библиотеки). Все объекты игры - код R*.
2) можно ли где-нибудь найти описание кодов CTask, иначе чем из команд assign behavior TaskID to object ?
я не встречал. было бы тоже интересно узнать. AFAIK, они используются в опкодах:
062E: unknown_get_actor 2@ task 1560 status_store_to 17@ // ret 7 if not found
Ясно. Буду копать...
Offline
По ходу дела, у меня возникла пара вопросов:
1) Есть где-нибудь более-менее внятное описание по представлению параметров опкодов в SCM ?
2) opcode 009A: %6d% = create_actor_pedtype %1d% model %2o% at %3d% %4d% %5d%
что есть pedtype ? Отдельно обрабатываются типы 6, 18, 19 - но, в оригинальном main.scm такого не обнаружено.
Offline
2listener:
Есть где-нибудь более-менее внятное описание по представлению параметров опкодов в SCM ?
тут есть немного
http://www.gtamodding.com/index.php?tit … verview%29
Но я могу расписать все в деталях, если нужно.
что есть pedtype ? Отдельно обрабатываются типы 6, 18, 19 - но, в оригинальном main.scm такого не обнаружено.
общий тип актера
0 Игрок 1 1 Игрок 2 2 Игрок 3 3 не используется 4 мужчина 5 женщина 6 полицейский 7 гангстер 1 8 гангстер 2 9 гангстер 3 10 гангстер 4 11 гангстер 5 12 гангстер 6 13 гангстер 7 14 гангстер 8 15 гангстер 9 16 гангстер 10 17 чувак, который продаёт наркоту 18 врач 19 пожарник 20 криминал 21 не нашёл 22 проститутка 23 специальный, как Смоук, Свит, Райдер и другие, которые используются в миссиях. 24 миссия 1 25 миссия 2 26 миссия 3 27 миссия 4 28 миссия 5 29 миссия 6 30 миссия 7 31 миссия 8
Offline
2Seemann:
Спасибо. Ссылочка эта у меня была, но потерялась.
Насчет деталей - попробую сам разобраться. (в первую очередь, хотелось нормально методы CScriptThread обозвать нормально).
По типам актеров - просто замечательно. Похоже, можно будет обозвать читабельно классы не только для Vehicle, но и для Ped-ов.
Offline