Предисловие
Однажды мне в руки попалась игра Unturned, построенная на движке Unity3d. Позже выяснилось, что она не лишена недостатков. Она находится в альфа версии, так что сервер даже не оповещает игроков о убийстве другого персонажа, что было не удобно в боях PvP. До этого случая у меня не было опыта работы с cil и реверс-инжинирингом c# приложений, тем более Unity.
Софт который нам понадобится
- JetBrains dotPeek (или .NET Reflector, но он не смог дизассемблировать то, что смог dotPeek)
- ildasm
- ilasm
- Редактор поддерживающий UTF-8 (например Notepad++)
Вскрываем программу
Код, который может быть интересен нам, находится в файле Assembly-CSharp.dll.
Сразу не раздумывая открываем его в dotPeek и видим примерно такую картину:
Пробуем декомпилировать весь код, загружаем проект в студию и получаем кучу ошибок. Visual Studio не смогла нормально обработать кучу переменных с названием в 1 UTF-8 символ.
Ладно, попробуем другим способом: открываем ildasm и загружаем в него наш файлик, нажимаем dump и сохраняем в удобное место. Мы получили кучу CIL кода. CIL код это как ассемблер для .NET, но намного легче.
Теперь нужно попробовать скомпилировать его обратно в dll, иначе дальнейшие действия окажутся напрасными.
Открываем консоль и пишем:
ilasm /DLL <путь до файла/файл.cil>
Ура! Получилось. Теперь мы можем менять код и компилировать его обратно.
Заходим в dotPeek и пишем в поиске die, находим некий Life::die(), смотрим код и понимаем что это не то.
Ладно, нажимаем правой клавишей на die, Usages of Symbol, и переходим в некий Life::damage()
В качестве аргумента эта функция принимает какую-то строку, нажимаем Usages of Symbol и переходим, например в shootPlayer(), смотрим в код:
string[] strArray = new string[7];int index1 = 0;string str2 = "You were shot in the ";strArray[index1] = str2;int index2 = 1;string str3 = str1;strArray[index2] = str3;int index3 = 2;string str4 = " with the ";strArray[index3] = str4;int index4 = 3;string name = ItemName.getName(this.GetComponent<Clothes>().\u0014);strArray[index4] = name;int index5 = 4;string str5 = " by ";strArray[index5] = str5;int index6 = 5;string str6 = this.GetComponent<Player>().\u0841.\u0887;strArray[index6] = str6;int index7 = 6;string str7 = "!";strArray[index7] = str7;string str8 = string.Concat(strArray);component.damage(num2, str8);
Это существенно облегчит нам работу, просто выведем «Имя DEAD (аргумент 2)».
Теперь ищем имя игрока, смотрим в тот же shootPlayer():
NetworkTools.kick(this.networkView.owner, "Kicking " + this.name + " for cheating their ammo.");
Теперь ищем тот же код в cil:
IL_002f: ldstr "Kicking "IL_0034: ldarg.0IL_0035: call instance string [UnityEngine]UnityEngine.Object::get_name()IL_003a: ldstr " for cheating their ammo."IL_003f: call string [mscorlib]System.String::Concat(string,string,string)IL_0044: call void NetworkTools::kick(valuetype [UnityEngine]UnityEngine.NetworkPlayer,string)
Осталось найти способ вывести сообщение в чат.
Ищем по cil файлу строку «connceted.»", находим вызов:
call void NetworkChat::sendAlert(string)
Патчим
Идем в Life::damage() в CIL.
Сразу после вызова die вставляем:
ldarg.0 //суём аргумент 0 в стекcall instance string [UnityEngine]UnityEngine.Object::get_name() //получаем имя(теперь оно в стеке)ldstr " DEAD (" //суём строку в стекldarg.2 //суём аргумент 2 в стек(строка со способом смерти)ldstr ")" //суём строку в стекcall string [mscorlib]System.String::Concat(string, //стыкуем их вместеstring,string,string)call void NetworkChat::sendAlert(string) //отсылаем в чат
Компилируем ilasm, заходим в игру, хостим сервер, входим, умираем и… в чате аж 3 сообщения, причем 1-е наше а 2 других нет.
Оказалось что после смерти игра каждый проход шлет damage() с уроном равным 1.
Я решил просто сделать проверку на урон и получилось вроде того:
ldarg.1 //суем аргумент 1 в стек(количество урона)ldc.i4.s 1 //суем число 1 в стекble.s IL_0061 //если меньше либо равно то перемещаемся на IL_0061ldarg.0 //суём аргумент 0 в стекcall instance string [UnityEngine]UnityEngine.Object::get_name() //получаем имя(теперь оно в стеке)ldstr " DEAD (" //суём строку в стекldarg.2 //суём аргумент 2 в стек(строка со способом смерти)ldstr ")" //суём строку в стекcall string [mscorlib]System.String::Concat(string, //стыкуем их вместеstring,string,string)call void NetworkChat::sendAlert(string) //отсылаем в чатIL_0061:
Заключение
Более подробно про инструкции CIL вы можете узнать из Википедии.
This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий