Лаборатория RuCTF 2011 Quals. PPC 300 Gamebot

folkyatina
, 03 March 2011

Дано: Исходники игры на Java и пример бота на C++. 

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

Ну, сначала посмотрим на игру.

Множество сфер разного радиуса, случайно расположенных на 2х мерном поле жрут друг друга, при чём та, что обедает - увеличивается. Сфера игрока посередине, зелененькая такая. Мы выигрываем, если становимся больше всех и проигрываем, если нас съедят - всё просто.

Для реализации бота существует объект BotStateSignal у которого каждый раунд игры дергается метод doGameState(), который в свою очередь вызывается у JNIной библиотечки, которую мы и пишем.

void JNICALL Java_org_ructf_quals_devouring_signal_BotStateSignal_doGameState(
JNIEnv *jenv,
 jobject self_signal,
 jobject list_all_units,
 jobject player_unit,
 jdouble field_width, jdouble field_height,
 jobject logic);

В качестве аргументов передаются:

  • список всех сфер в игре List<Sphere> ( jobject list_all_units )
  • ссылка на объект Sphere представляющий игрока ( jobject player_unit)
  • размеры поля (jdouble field_width, jdouble field_height)
  • класс с интерфейсом IAction,  который позвпляет нам плеваться вызывая метод splashTo(angle, strength) и уменьшая свой радиус пропорционально плевку.

Первое что приходит на ум - написать A*. Но это же не ACM, поэтому мы смело отметаем первую мысль и приглядываемся повнимательнее. У нас есть ссылка на наш инстанс Сферы, по полю r, которого в конце каждого хода система проверяет, выйграли мы или нет. Но поле r приватное, и публичен только геттер. В голове тут же всплывает слово reflection. Немного гугления и вуаля.

jclass sphere = jenv->GetObjectClass(player_unit);
//получаем java класс Sphere
jclass field_class = jenv->FindClass("Ljava/lang/reflect/Field;");
//находим класс Field.
jmethodID set_access = jenv->GetMethodID(field_class, "setAccessible", "(Z)V");
jmethodID set_double = jenv->GetMethodID(field_class, "setDouble", "(Ljava/lang/Object;D)V");
//получаем methodID нужных нам методов класса Field.
jfieldID r_f = jenv->GetFieldID(sphere, "r", "D");
// fieldID нашего заветного field'а
jobject r_field = jenv->ToReflectedField(sphere, r_f, JNI_FALSE); 
//fieldId превращается... в java объект класса Field, привязанный к нужному нам полю.
jenv->CallVoidMethod(r_field, set_access, JNI_TRUE);
//Выключаем приватность этого поля setAccessible(true);
jenv->CallVoidMethod(r_field, set_double, player_unit, 1000.0);
//И выставляем нужное нам значение setDouble(Object obj, double value);

И никаких A* - грязный хак и чистая победа в первом раунде.

P.S. Advisory - можно было бы закрыть передавая в метод копии объектов либо списки примитивов, а не ссылки на реальные объекты игровой логики. Так же пробовался саботаж с удалением всех врагов из списка передаваемого в аргументах.