diff --git a/README.md b/README.md index ef43f5a..e7976dd 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/asconfig.json b/asconfig.json index a7ce818..26d35b8 100644 --- a/asconfig.json +++ b/asconfig.json @@ -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" diff --git a/src/ServerboundLeakerSocket.as.reference b/src/ServerboundLeakerSocket.as similarity index 90% rename from src/ServerboundLeakerSocket.as.reference rename to src/ServerboundLeakerSocket.as index 219c6fb..6ae7e92 100644 --- a/src/ServerboundLeakerSocket.as.reference +++ b/src/ServerboundLeakerSocket.as @@ -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(); diff --git a/src/leakers/Leaker.as b/src/leakers/Leaker.as index 5b8aee7..00f56b2 100644 --- a/src/leakers/Leaker.as +++ b/src/leakers/Leaker.as @@ -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 { /* @@ -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(); @@ -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; } } @@ -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); @@ -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]; @@ -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)); diff --git a/src/leakers/TransformiceLeaker.as b/src/leakers/TransformiceLeaker.as index 311c8db..76108c3 100644 --- a/src/leakers/TransformiceLeaker.as +++ b/src/leakers/TransformiceLeaker.as @@ -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") { @@ -57,11 +48,18 @@ 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; } } @@ -69,7 +67,7 @@ package leakers { 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; @@ -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; }