Skip to content

Commit

Permalink
Update for Transformice 1.808 and Dead Maze 1.54
Browse files Browse the repository at this point in the history
  • Loading branch information
friedkeenan committed Mar 28, 2024
1 parent 40cd32f commit dea18f7
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 12 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ A utility for obtaining the hardcoded secrets within the Transformice client.

To build, you should use the [asconfig.json](https://github.com/friedkeenan/tfm-secrets-leaker/blob/main/asconfig.json) file to compile the `TFMSecretsLeaker.swf` file. This can be done with [vscode-as3mxml](https://github.com/BowlerHatLLC/vscode-as3mxml) or [asconfigc](https://www.npmjs.com/package/asconfigc).

You will also need to place the SWC files for the following libraries under a `lib` folder at the same level as the `asconfig.json` file:

- [as3commons-bytecode-1.1.1](https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/as3-commons/as3commons-bytecode-1.1.1.swc)
- [as3commons-lang-0.3.7](https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/as3-commons/as3commons-lang-0.3.7.swc)
- [as3commons-reflect-1.6.4](https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/as3-commons/as3commons-reflect-1.6.4.swc)

If you wish to save yourself the hassle, then there is also a pre-built SWF in the [releases](https://github.com/friedkeenan/tfm-secrets-leaker/releases) of this repo.

## Usage
Expand Down
8 changes: 7 additions & 1 deletion asconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@
"default-size": {
"width": 800,
"height": 600
}
},

"library-path": [
"lib/as3commons-bytecode-1.1.1.swc",
"lib/as3commons-lang-0.3.7.swc",
"lib/as3commons-reflect-1.6.4.swc"
]
},

"mainClass": "TFMSecretsLeaker"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ package {
import flash.utils.ByteArray;

public class ServerboundLeakerSocket extends Socket {
/*
NOTE: This class serves as a reference for what
the generated leaker socket class looks like.
*/

private var flush_callback: Function;
private var written_bytes: ByteArray = new ByteArray();

Expand Down
85 changes: 85 additions & 0 deletions src/leakers/DeadMazeLeaker.as
Original file line number Diff line number Diff line change
@@ -1,7 +1,92 @@
package leakers {
import flash.system.ApplicationDomain;
import flash.utils.describeType;
import flash.net.Socket;

public class DeadMazeLeaker extends Leaker {
private var socket_dict_name: String;

public function DeadMazeLeaker() {
super("http://www.deadmaze.com/alpha/deadmeat.swf", true);
}

private function get_socket_method_name(description: XML) : String {
for each (var method: * in description.elements("method")) {
if (method.attribute("returnType") == "flash.net::Socket") {
return method.attribute("name");
}
}

return null;
}

private function get_socket_prop_name(description: XML) : void {
for each (var variable: * in description.elements("variable")) {
if (variable.attribute("type") == "flash.net::Socket") {
this.socket_prop_name = variable.attribute("name");

return;
}
}
}

protected override function process_socket_info(domain: ApplicationDomain, _: XML) : void {
var document: * = this.document();
var description: * = describeType(document);

/* Load a socket into the dictionary. */
document[this.get_socket_method_name(description)](-1);

for each (var variable: * in description.elements("variable")) {
if (variable.attribute("type") != "flash.utils::Dictionary") {
continue;
}

var dictionary: * = document[variable.attribute("name")];

if (dictionary == null) {
continue;
}

var maybe_socket: * = dictionary[-1];
if (maybe_socket == null) {
continue;
}

if (maybe_socket is Socket) {
delete dictionary[-1];

this.socket_dict_name = variable.attribute("name");

this.get_socket_prop_name(describeType(maybe_socket));

this.build_leaker_socket(domain, "flash.net::Socket");

return;
}
}
}

protected override function get_connection_socket(instance: *) : Socket {
for each (var socket: * in this.document()[this.socket_dict_name]) {
return socket[this.socket_prop_name];
}

return null;
}

protected override function set_connection_socket(instance: *, socket: Socket) : void {
var dictionary: * = this.document()[this.socket_dict_name];

for (var key: * in dictionary) {
dictionary[key][this.socket_prop_name] = socket;

return;
}
}

protected override function auth_key_return() : String {
return "*";
}
}
}
138 changes: 135 additions & 3 deletions src/leakers/Leaker.as
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@ package leakers {
import flash.utils.ByteArray;
import flash.system.Capabilities;
import flash.system.ApplicationDomain;
import org.as3commons.bytecode.emit.impl.AbcBuilder;
import org.as3commons.bytecode.emit.IClassBuilder;
import org.as3commons.bytecode.emit.IAbcBuilder;
import org.as3commons.bytecode.abc.enum.Opcode;
import org.as3commons.bytecode.abc.QualifiedName;
import org.as3commons.bytecode.abc.LNamespace;
import org.as3commons.bytecode.abc.enum.NamespaceKind;
import org.as3commons.bytecode.emit.ICtorBuilder;
import org.as3commons.bytecode.emit.IAccessorBuilder;
import org.as3commons.reflect.AccessorAccess;
import org.as3commons.bytecode.emit.IMethodBuilder;
import org.as3commons.bytecode.emit.IPackageBuilder;
import org.as3commons.bytecode.emit.event.AccessorBuilderEvent;
import org.as3commons.bytecode.emit.impl.MethodBuilder;
import org.as3commons.bytecode.emit.enum.MemberVisibility;

public class Leaker extends Sprite {
/*
Expand Down Expand Up @@ -41,6 +56,8 @@ package leakers {

private var logging_class_info: *;

private var leaker_socket_class: Class = null;

protected var socket_prop_name: String;
private var connection_class_info: *;

Expand Down Expand Up @@ -218,11 +235,122 @@ package leakers {
return false;
}

protected function process_socket_info(description: XML) : void {
protected function build_leaker_socket(domain: ApplicationDomain, parent_name: String) : void {
var abc: IAbcBuilder = new AbcBuilder();
var pkg: IPackageBuilder = abc.definePackage("");

var cls: IClassBuilder = pkg.defineClass("ServerboundLeakerSocket", parent_name);

cls.defineProperty("flush_callback", "Function");
cls.defineProperty("written_bytes", "flash.utils::ByteArray");

var blank_namespace: * = new LNamespace(NamespaceKind.PACKAGE_NAMESPACE, "");

var written_bytes: * = new QualifiedName("written_bytes", blank_namespace);
var flush_callback: * = new QualifiedName("flush_callback", blank_namespace);

var bytearray: * = new QualifiedName("ByteArray", LNamespace.FLASH_UTILS);
var bytearray_clear: * = new QualifiedName("clear", blank_namespace);
var bytearray_writeBytes: * = new QualifiedName("writeBytes", blank_namespace);
var bytearray_position: * = new QualifiedName("position", blank_namespace);

var constructor: ICtorBuilder = cls.defineConstructor();

constructor.defineArgument("Function");

/* Construct 'written_bytes' and assign 'flush_callback'. */
constructor
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.pushscope)
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.constructsuper, [0])
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.findpropstrict, [bytearray])
.addOpcode(Opcode.constructprop, [bytearray, 0])
.addOpcode(Opcode.setproperty, [written_bytes])
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.getlocal_1)
.addOpcode(Opcode.setproperty, [flush_callback])
.addOpcode(Opcode.returnvoid);

var connected: IAccessorBuilder = cls.defineAccessor("connected", "Boolean");

connected.access = AccessorAccess.READ_ONLY;
connected.createPrivateProperty = false;

connected.addEventListener(AccessorBuilderEvent.BUILD_GETTER, function (event: AccessorBuilderEvent) : void {
var method: IMethodBuilder = new MethodBuilder("connected");

method.isOverride = true;
method.visibility = MemberVisibility.PUBLIC;
method.returnType = "Boolean";

/* Always return true for 'connected'. */
method
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.pushscope)
.addOpcode(Opcode.pushtrue)
.addOpcode(Opcode.returnvalue);

event.builder = method;
});

var writeBytes: IMethodBuilder = cls.defineMethod("writeBytes");

writeBytes.isOverride = true;

writeBytes.defineArgument("flash.utils::ByteArray");
writeBytes.defineArgument("uint", true, 0);
writeBytes.defineArgument("uint", true, 0);

/* Clear 'written_bytes' then forward onto its 'writeBytes' method then reset its position. */
writeBytes
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.pushscope)
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.getproperty, [written_bytes])
.addOpcode(Opcode.callpropvoid, [bytearray_clear, 0])
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.getproperty, [written_bytes])
.addOpcode(Opcode.getlocal_1)
.addOpcode(Opcode.getlocal_2)
.addOpcode(Opcode.getlocal_3)
.addOpcode(Opcode.callpropvoid, [bytearray_writeBytes, 3])
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.getproperty, [written_bytes])
.addOpcode(Opcode.pushbyte, [0])
.addOpcode(Opcode.setproperty, [bytearray_position])
.addOpcode(Opcode.returnvoid);

var flush: IMethodBuilder = cls.defineMethod("flush");

flush.isOverride = true;

/* Call 'flush_callback' with 'written_bytes'. */
flush
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.pushscope)
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.getlocal_0)
.addOpcode(Opcode.getproperty, [written_bytes])
.addOpcode(Opcode.callpropvoid, [flush_callback, 1])
.addOpcode(Opcode.returnvoid);

abc.addEventListener(Event.COMPLETE, this.loaded_leaker_socket);
abc.buildAndLoad(domain, domain);
}

private function loaded_leaker_socket(event: Event) : void {
this.leaker_socket_class = this.game_domain().getDefinition("ServerboundLeakerSocket") as Class;
}

protected function process_socket_info(domain: ApplicationDomain, description: XML) : void {
for each (var variable: * in description.elements("factory").elements("variable")) {
if (variable.attribute("type") == "flash.net::Socket") {
this.socket_prop_name = variable.attribute("name");

this.build_leaker_socket(domain, "flash.net::Socket");

return;
}
}
Expand Down Expand Up @@ -312,7 +440,7 @@ package leakers {
continue;
}

this.process_socket_info(description);
this.process_socket_info(domain, description);

var address_prop_name: * = get_address_prop_name(description);
var possible_ports_prop_names: * = get_possible_ports_properties(description);
Expand Down Expand Up @@ -340,6 +468,10 @@ package leakers {
}

private function try_replace_socket(event: Event) : void {
if (this.leaker_socket_class == null) {
return;
}

var klass: Class = this.connection_class_info.klass;
var instance: * = klass[this.connection_class_info.instance_name];

Expand Down Expand Up @@ -394,7 +526,7 @@ package leakers {
Replace the connection's socket with our own socket
which will keep track of the sent packets for us.
*/
this.set_connection_socket(instance, new ServerboundLeakerSocket(this.on_sent_packet));
this.set_connection_socket(instance, new this.leaker_socket_class(this.on_sent_packet));

/* Dispatch fake connection event to trigger handshake packet. */
socket.dispatchEvent(new Event(Event.CONNECT));
Expand Down
31 changes: 23 additions & 8 deletions src/leakers/TransformiceLeaker.as
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package leakers {
import flash.utils.describeType;
import flash.net.Socket;
import flash.system.ApplicationDomain;
import flash.utils.getQualifiedClassName;

public class TransformiceLeaker extends Leaker {
private var socket_dict_name: String;
Expand All @@ -10,32 +11,46 @@ package leakers {
super("http://www.transformice.com/Transformice.swf", true);
}

private function get_socket_method_name(description: XML) : String {
private function get_socket_method_name(domain: ApplicationDomain, description: XML) : String {
for each (var method: * in description.elements("method")) {
if (method.attribute("returnType") == "flash.net::Socket") {
return method.attribute("name");
var parameters: * = method.elements("parameter");
if (parameters.length() != 1) {
continue;
}

if (parameters[0].attribute("type") != "Number") {
continue;
}

var return_type: * = method.attribute("returnType");
if (return_type == "void" || return_type == "*") {
continue;
}

this.build_leaker_socket(domain, return_type);

return method.attribute("name");
}

return null;
}

private function get_socket_prop_name(description: XML) : void {
private function get_socket_prop_name(description: XML, type_name: String) : void {
for each (var variable: * in description.elements("variable")) {
if (variable.attribute("type") == "flash.net::Socket") {
if (variable.attribute("type") == type_name) {
this.socket_prop_name = variable.attribute("name");

return;
}
}
}

protected override function process_socket_info(_: XML) : void {
protected override function process_socket_info(domain: ApplicationDomain, _: XML) : void {
var document: * = this.document();
var description: * = describeType(document);

/* Load a socket into the dictionary. */
document[this.get_socket_method_name(description)](-1);
var real_socket: * = document[this.get_socket_method_name(domain, description)](-1);

for each (var variable: * in description.elements("variable")) {
if (variable.attribute("type") != "flash.utils::Dictionary") {
Expand All @@ -58,7 +73,7 @@ package leakers {

this.socket_dict_name = variable.attribute("name");

this.get_socket_prop_name(describeType(maybe_socket));
this.get_socket_prop_name(describeType(maybe_socket), getQualifiedClassName(real_socket));

return;
}
Expand Down

0 comments on commit dea18f7

Please sign in to comment.