Skip to content

Commit

Permalink
Update for Transformice 1.807
Browse files Browse the repository at this point in the history
  • Loading branch information
friedkeenan committed Mar 22, 2024
1 parent db43c97 commit 40cd32f
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 179 deletions.
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ 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: 1 addition & 7 deletions asconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,7 @@
"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,11 +3,6 @@ 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
140 changes: 4 additions & 136 deletions src/leakers/Leaker.as
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,6 @@ 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 @@ -56,11 +41,9 @@ package leakers {

private var logging_class_info: *;

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

private var leaker_socket_class: Class = null;

private var server_address: String;
private var main_ports: Array = new Array();

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

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 {
protected function process_socket_info(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 @@ -440,7 +312,7 @@ package leakers {
continue;
}

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

var address_prop_name: * = get_address_prop_name(description);
var possible_ports_prop_names: * = get_possible_ports_properties(description);
Expand Down Expand Up @@ -468,10 +340,6 @@ 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 @@ -526,7 +394,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 this.leaker_socket_class(this.on_sent_packet));
this.set_connection_socket(instance, new ServerboundLeakerSocket(this.on_sent_packet));

/* Dispatch fake connection event to trigger handshake packet. */
socket.dispatchEvent(new Event(Event.CONNECT));
Expand Down
48 changes: 23 additions & 25 deletions src/leakers/TransformiceLeaker.as
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,32 @@ package leakers {
super("http://www.transformice.com/Transformice.swf", true);
}

private function is_socket_class(klass: Class) : Boolean {
var description: * = describeType(klass);

for each (var parent: * in description.elements("factory").elements("extendsClass")) {
if (parent.attribute("type") == "flash.net::Socket") {
return 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 false;
return null;
}

private function get_socket_method_name(domain: ApplicationDomain, description: XML) : String {
for each (var method: * in description.elements("method")) {
try {
var return_type: * = domain.getDefinition(method.attribute("returnType"));
} catch (ReferenceError) {
continue;
}

if (this.is_socket_class(return_type)) {
this.build_leaker_socket(domain, method.attribute("returnType"));
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 method.attribute("name");
return;
}
}

return null;
}

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

var socket: * = document[this.get_socket_method_name(domain, description)](-1);
/* 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") {
Expand All @@ -57,19 +48,26 @@ package leakers {
continue;
}

if (dictionary[-1] == socket) {
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));

return;
}
}
}

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

return null;
Expand All @@ -79,7 +77,7 @@ package leakers {
var dictionary: * = this.document()[this.socket_dict_name];

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

return;
}
Expand Down

0 comments on commit 40cd32f

Please sign in to comment.