RE: Kingdom Rush Frontiers

Hace poco en youtube me encontré este video de 0xd4y donde enseña a crackear juegos flash usando JPEX Free Flash Decompiler (ffdec) poniendo de ejemplo a Swords and Sandals 2. Así que se me ocurrió hacer lo mismo pero con un título que me gusta bastante: Kingdom Rush. Esta es mi primera incursión en el game hacking, y honestamente me pareció una experiencia satisfactoria.


Índice

¿Qué es un juego Flash?

Un juego flash por lo general está conformado por un único archivo .swf (Small Web Format o ShockWaveFlash) que contiene todo el código y assets (imágenes, frames, audio, fuentes, etc.) y es ejecutable usando un emulador de este formato. En el pasado se usaba Macromedia Flash Player / Adobe Flash Player; tras la muerte del soporte a esta herramienta por parte de Adobe surgieron nuevas alternativas como la usada en este trabajo.

El código está escrito en Adobe ActionScript, un lenguaje de programación introducido igualmente por Adobe para su plataforma Adobe Flash. Este lenguaje tiene similitudes con otros lenguajes de programación orientado a objetos. Si el lector está familiarizado con Javascript, Java, C# o similar, encontrará este código bastante sencillo de entender.

Herramientas utilizadas

Como decompilador empleé el susodicho ffdec. También funciona como un debugger usando un emulador pero me limité a hacer solo análisis estático.

Como emulador usé ruffle , porque fue el primero que encontré y cuenta con la comodidad de estar disponible también como extensión para el navegador.

Enlace de descarga del juego: https://archive.org/details/kingdom-rush-frontie-15717 (Kingdom Rush Frontiers versión 1.1.6a)

Hackeando el juego

Obtener todas las estrellas

Empecé buscando en scripts con nombres que dieran la impresión de usar las estrellas del juego como GameUpgrades y MenuUpgrades.

No encontré nada en GameUpgrades pero en MenuUpgrades vi que se usa una variable game.stars para realizar cálculos cuando se elige una mejora, se deshace la última, o se reinicia el arbol de mejoras:

Luego de cambiar los valores iniciales de game.stars y game.starsWon podemos comentar o borrar la línea de this.game.stars -= param1.stars; para mantener el número de estrellas inmutable.

Busqué la cadena “stars” con “/Tools/Text Search”:

Encontré que son definidas en el script §_-BQ§:

El máximo de estrellas que se pueden obtener son, al parecer, 77:

Cambiamos los valores de stars y starsWon (recordar que se usa en el reset de MenuUpgrades y tal vez en otras validaciones, por lo que es conveniente cambiarla también) al máximo de estrellas.

Antes de revisar los cambios actualicé en el script MenuUpgrades las líneas this.game.stars += §true const do§(this.§_-b5§.getChildAt(_loc2_[_loc3_])).stars; y this.game.stars -= param1.stars; por this.game.stars = this.game.stars; para mantener el número de estrellas inmutable.

Noté que §_-BQ§ era como un script para inicializar valores al crear un nuevo slot local y resultó ser el punto de partida de casi todas mis modificaciones. Veamos ahora:

Desbloquear todos los niveles

Aunque tengamos todas las estrellas solo nos permite jugar el primer nivel. Estuve un rato saltando de un lado a otro, revisando, y al final encontre que en §_-BQ§ hay un array data.levels que se inicializa con algunas constantes “LEVEL_…”:

Toma valores de una clase §true final§, que se encuentra en un script del mismo nombre con un constructor que inicializa los niveles como deshabilitados:

También vi las declaraciones de las variables locales y estas fueron las líneas que cambié en §true final§:

1
2
3
4
5
6
7
8
9
40: public var stars:int;                ===> public var stars:int = 3;
44: public var heroicMode:Boolean; ===> public var heroicMode:Boolean = true;
46: public var ironMode:Boolean; ===> public var ironMode:Boolean = true;
48: public var heroicModeView:Boolean; ===> public var heroicModeView:Boolean = true;
54: public var heroicModeWin:Boolean; ===> public var heroicModeWin:Boolean = true;
56: public var ironModeView:Boolean; ===> public var ironModeView:Boolean = true;
62: public var ironModeWin:Boolean; ===> public var ironModeWin:Boolean = true;
79: this.stars = param3; ===> this.stars = 3
85: this.levelStatus = LEVEL_DISABLED; ===> this.levelStatus = LEVEL_ENABLED_COMPLETED;

Para que se mantenga al recargar el slot cambié esta inicializacion de los niveles en §_-BQ§:

Para evitar que el número de estrellas pasase de 77 tambien modifiqué algunas líneas en una serie de scripts.

§override const static§:

MenuVictoryHeroic:

MenuVictoryIron:

ComicEnd:


Prefiero introducir una línea que no hace nada a dejarlo en blanco porque me ayuda a recordar cual era la lógica que existía en ese fragmento de código.

Desbloquear todas las mejoras

Fui a las declaraciones de variables en el script Game Upgrades y actualicé al nivel maximo (5) las 6 mejoras. Además de los niveles de mejora cambié los booleanos que parecían ser nombres de mejoras (algunos estaban ofuscados), al final deben lucir así:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public var archersUpLevel:int = 5;

public var archersUpSalvage:Boolean = true;

public var archersUpEagleEye:Boolean = true;

public var archersUpPiercing:Boolean = true;

public var archersUpFarShots:Boolean = true;

public var archersUpPrecision:Boolean = true;

public var barracksUpLevel:int = 5;

public var barracksUpSurvival:Boolean = true;

public var barracksUpBetterArmor:Boolean = true;

public var barracksUpImprovedDeployment:Boolean = true;

public var barracksUpBarbedArmor:Boolean = true;

public var barracksUpSurvival2:Boolean = true;

public var magesUpLevel:int = 5;

public var §final const implements§:Boolean = true;

public var §_-G0§:Boolean = true;

public var §_-i7§:Boolean = true;

public var §_-dC§:Boolean = true;

public var magesUpSlowCurse:Boolean = true;

public var magesUpArcaneShatterDamage:int = 3;

public var engineersUpLevel:int = 5;

public var engineersUpConcentratedFire:Boolean = true;

public var engineersUpRangeFinder:Boolean = true;

public var engineersUpFieldLogistics:Boolean = true;

public var engineersUpIndustrialization:Boolean = true;

public var engineersUpEfficiency:Boolean = true;

public var rainUpLevel:int = 5;

public var rainUpBlazingSkies:Boolean = true;

public var §_-M9§:Boolean = true;

public var §_-YK§:Boolean = true;

public var §continue const final§:Boolean = true;

public var rainUpCataclysm:Boolean = true;

public var reinforcementLevel:int = 5;

Bola de fuego y Refuerzos instantáneos

Filtrando por la palabra “fireball” eventualmente encontré que el script §_-Mm§ contiene las características de muchos elementos del juego, incluyendo las habilidades fireball y reinforcement (esta con valores para varios niveles de la habilidad).

Podemos cambiar el tiempo de espera de los meteoritos aquí:

Elegí 10 frames porque hay dos mejoras que reducen el coolDown en 5 segundos como se puede ver aquí:

Para cambiar el tiempo de espera de los refuerzos al máximo nivel hacemos esto:

Desbloquear a todos los héroes

Para desbloquear a Mirage y a Cronan cambié las variables showedUnlockedMirage:Boolean y showedUnlockedCronan:Boolean a true en §_-BQ§.

Para desbloquear al resto supuestamente debía usar un slot online pero buscando por nombres de uno de estos héroes me encontré que en el scriptif const function§, en su función checkPremiumContent hay un switch que devuelve false para Alric (el héroe disponible al principio):

Entonces cambié todas esas validaciones que se hacen contra “purchasedhéroes” por false, rezando porque me devolviera todos los héroes desbloqueados.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
switch(param1)
{
case "captain":
return false;
case "nivus":
return false;
case "dierdre":
return false;
case "grawl":
return false;
case "shatra":
return false;
case "ashbite":
return false;
case "cronan":
return false;
case "mirage":
return false;
case "alric":
return false;
default:
return true;
}

Mejorar los héroes al máximo

En el script §_-2i§ se observa que los héroes tienen un arreglo de habilidades, por ejemplo, Nivus:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var _loc22_:* = new Object();
_loc22_.description = ["Casts 3 homing magic missiles that never miss and deal 12 damage each.","Casts 5 homing magic missiles that never miss and deal 18 damage each.","Casts 7 homing magic missiles that never miss and deal 24 damage each.","Casts 7 homing magic missiles that never miss and deal 24 damage each."];
_loc22_.level = 0;
_loc22_.cost = [1,2,3,0];
_loc22_.name = ["Magic Missile","Magic Missile II","Magic Missile III","Magic Missile III"];
var _loc23_:* = new Object();
_loc23_.description = ["Casts an overcharged ray that bounces off up to 2 targets.","Casts an overcharged ray that bounces off up to 3 targets.","Casts an overcharged ray that bounces off up to 4 targets.","Casts an overcharged ray that bounces off up to 4 targets."];
_loc23_.level = 0;
_loc23_.cost = [2,2,2,0];
_loc23_.name = ["Chain Spell","Chain Spell II","Chain Spell III","Chain Spell III"];
var _loc24_:* = new Object();
_loc24_.description = ["Desintegrate enemies in range with a combined life of 170 or less.","Desintegrate enemies in range with a combined life of 330 or less.","Desintegrate enemies in range with a combined life of 480 or less.","Desintegrate enemies in range with a combined life of 480 or less."];
_loc24_.level = 0;
_loc24_.cost = [3,3,3,0];
_loc24_.name = ["Desintegrate","Desintegrate II","Desintegrate III","Desintegrate III"];
var _loc25_:* = new Object();
_loc25_.description = ["Increases Nivus\' attack range by 10%.","Increases Nivus\' attack range by 20%.","Increases Nivus\' attack range by 30%.","Increases Nivus\' attack range by 30%."];
_loc25_.level = 0;
_loc25_.cost = [1,1,1,0];
_loc25_.name = ["Arcane Reach","Arcane Reach II","Arcane Reach III","Arcane Reach III"];
var _loc26_:* = new Object();
_loc26_.description = ["Increases Nivus\' base attack damage by 2 points.","Increases Nivus\' base attack damage by 4 points.","Increases Nivus\' base attack damage by 6 points.","Increases Nivus\' base attack damage by 6 points."];
_loc26_.level = 0;
_loc26_.cost = [1,2,3,0];
_loc26_.name = ["Arcane Focus","Arcane Focus II","Arcane Focus III","Arcane Focus III"];
this.heroNivus.name = "nivus";
this.heroNivus.heroClass = "Grand Wizard";
this.heroNivus.health = "160";
this.heroNivus.armor = "None";
this.heroNivus.damage = "12 - 41";
this.heroNivus.speed = "Slow";
this.heroNivus.level = 4;
this.heroNivus.xp = this.master_xp[this.heroNivus.level - 1];
this.heroNivus.type = "magic";
this.heroNivus.skillArray = [_loc22_,_loc23_,_loc24_,_loc25_,_loc26_];
this.heroNivus.skill1 = _loc22_;
this.heroNivus.skill2 = _loc23_;
this.heroNivus.skill3 = _loc24_;
this.heroNivus.skill4 = _loc25_;
this.heroNivus.skill5 = _loc26_;
this.heroNivus.skillPoints = this.§_-oq§(this.heroNivus);

Su nivel inicial es 4 y sus habilidades comienzan en el nivel 0. Solo debemos cambiar su nivel, el nivel de todas sus habilidades al máximo(3) y sus puntos de habilidad a 0 (porque los hemos usado todos y con un reset los podemos recuperar). Haremos esto para cada héroe.

Intenté subir al capitán a un nivel muy alto y se convirtió en Mister Not-A-Number:


En la imagen dice nivel 10 pero en realidad usé un número más alto, parece que este es el límite.

Desbloquear la enciclopedia completa

Ir al script §null throw§ e inicializar todas las variables públicas “notificationTower…” y “notificationEnemy…” a true.

Conclusiones

Como habrá observado crackear juegos flash es bastante sencillo, solo requiere un poco de tiempo y algo de intuición que se gana programando (eso y que el desarrollador no sea mediocre estructurando su código o nombrando sus variables, en cuyo caso la pasará mal).

No cubrí en este artículo como alterar el oro ni las capacidades de las torres y enemigos. Eso es algo que seguramente el lector puede averiguar por su cuenta realizando un análisis similar.

Eso es todo.

Enlaces