...

четверг, 21 августа 2014 г.

[Из песочницы] Реверс-инжиниринг и патч игры на Unity3d

Статья ориентирована на аудиторию, не имеющую какого-либо опыта. В ней не содержится описание каких-либо взломов и «плохих» патчей.

Предисловие




Однажды мне в руки попалась игра Unturned, построенная на движке Unity3d. Позже выяснилось, что она не лишена недостатков. Она находится в альфа версии, так что сервер даже не оповещает игроков о убийстве другого персонажа, что было не удобно в боях PvP. До этого случая у меня не было опыта работы с cil и реверс-инжинирингом c# приложений, тем более Unity.



Софт который нам понадобится





  • JetBrains dotPeek (или .NET Reflector, но он не смог дизассемблировать то, что смог dotPeek)

  • ildasm

  • ilasm

  • Редактор поддерживающий UTF-8 (например Notepad++)


Вскрываем программу




Код, который может быть интересен нам, находится в файле Assembly-CSharp.dll.

Сразу не раздумывая открываем его в dotPeek и видим примерно такую картину:

image


Пробуем декомпилировать весь код, загружаем проект в студию и получаем кучу ошибок. 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.0

IL_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_0061

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) //отсылаем в чат

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.


Комментариев нет:

Отправить комментарий