hackademiC
Member
Hi all,
I've been playing around with RABCDasm for the last week or so and I'm attempting to disable the RSA and RC4 in the Habbo.swf so that I'm able to use it without having to encrypt and decrypt. I know that I can go and get another SWF that is already patched with headers/structures and use that, but I want to do this myself manually so that I can learn it and do it again for any future releases!
The client version: PRODUCTION-202206221407-523648813
Anyhow, I've been able to remove the host protection quite easily by looking over the methods and returning true and false where needed, however disabling the RC4 and RSA has been the most difficult so far and I'm not making much progress.
Bit of a long shot, but if anyone has any knowledge of walking over AS3 byte instructions and can help, it would be awesome!
So far, like I said, I've patched the host protection stuff (that was easy!) and the client connects and sends the <policy-file-request/> packet, disconnects and then sends the ClientHello and InitCrypto packets just like normal. All is well, I'm also able to other miscellaneous send packets back (alert windows, and broadcast message windows) and the message box/alert boxes appear like normal too.
The issue is, no matter what I'm doing, the client never, ever will send the SSOTicketMessage packet. I've tried removing the line of code that calls the InitCrypto to the server, and replace it with the function that calls the SSO function, however even though with the line present that calls the SSO function, it doesn't send it.
I'm pretty sure what is occuring is this. The function that is calling the SSOTicket packet, is calling it and then calling SocketConnection.send() which encodes the packet (with header) and checks if the RC4 variable is null or not, and then if it's null, the function returns back and doesn't send. The problem is, I've tried editing the .send() function so that instead of .writeBytes(encryptedMessage), I am setting the variable that is returned from .encode() to a local7 for example instead of the original local4, and then passing that same variable through to .writeBytes(). I've removed the null check, by passing ifeq rather than ifne to make it skip the null check. However, once I do that, it either just doesn't send the packet, or I'm getting a stack depth inbalance.
All of these functions are in the same class, thus, in theory, should be able to call any of the methods I like from any of the other methods, without getting any reference errors.
This is what I've found so far:
This is obviously listening for those incoming messages, and then calling the method that the event is linked too. From this, I know that, the function which should kick off the SSOTicket is in the function _SafeStr_20029().
This is function _SafeStr_20029()
From this, I know I can either send a packet which will fire the method _SafeStr_20029() or copy the last line which calls the SSO event and move it to another function.
I then went on and found where the ClientHello and InitCrypto are called from. They are called when a client connects (surprise, surprise)
Based on the first snippet, I know that function _SafeStr_20023() is called when the socket connects.
This is _SafeStr_20023()
I then went ahead in the byte code, and replaced the InitDiffieHandshakeMessage and put the this._SafeStr_14849._SafeStr_9533(_local_2); in it's place. I've confirmed this by assembling and checking the AS3 representation and the function is valid at least. The lines with - Removed, are lines I've removed and also left there both times to see if it makes a difference. I've also tried setting _SafeStr_19965 to true and _SafeStr_20036 to false and seems to make no difference.
Basically, I just can't work out why the SSOTicketMessage is never sent, and I'm pretty confident it has something to do with the .send() function which is what is really sending the SSO message as seen below:
k is the socket object, and it's calling the .send() function which is the same function calling the encrypt function. Where as the ClientHello and InitCrypto is being called by another function _SafeStr_16233(), which is doing almost the same thing, but instead it's not checking for null on the RC4 object or passing the message into any other methods, other than .writeBytes().
And lastly, this is the .send() function and the .16233() function which does the encoding and then sending the packet/message.
Hopefully, this makes sense, and someone can help. Otherwise, when I find out, I'll post my results!
The SWF is an original with only host protection patched. RC4 and RSA keys are as original and haven't been touched.
SWF Version: PRODUCTION-202206221407-523648813
The SWF link: https://mega.nz/file/mogCFYrQ#zyme8fCsZtVc8Yx8scxol9uCRSfOMKNZXdz0SB0bCQs
I've been playing around with RABCDasm for the last week or so and I'm attempting to disable the RSA and RC4 in the Habbo.swf so that I'm able to use it without having to encrypt and decrypt. I know that I can go and get another SWF that is already patched with headers/structures and use that, but I want to do this myself manually so that I can learn it and do it again for any future releases!
The client version: PRODUCTION-202206221407-523648813
Anyhow, I've been able to remove the host protection quite easily by looking over the methods and returning true and false where needed, however disabling the RC4 and RSA has been the most difficult so far and I'm not making much progress.
Bit of a long shot, but if anyone has any knowledge of walking over AS3 byte instructions and can help, it would be awesome!
So far, like I said, I've patched the host protection stuff (that was easy!) and the client connects and sends the <policy-file-request/> packet, disconnects and then sends the ClientHello and InitCrypto packets just like normal. All is well, I'm also able to other miscellaneous send packets back (alert windows, and broadcast message windows) and the message box/alert boxes appear like normal too.
The issue is, no matter what I'm doing, the client never, ever will send the SSOTicketMessage packet. I've tried removing the line of code that calls the InitCrypto to the server, and replace it with the function that calls the SSO function, however even though with the line present that calls the SSO function, it doesn't send it.
I'm pretty sure what is occuring is this. The function that is calling the SSOTicket packet, is calling it and then calling SocketConnection.send() which encodes the packet (with header) and checks if the RC4 variable is null or not, and then if it's null, the function returns back and doesn't send. The problem is, I've tried editing the .send() function so that instead of .writeBytes(encryptedMessage), I am setting the variable that is returned from .encode() to a local7 for example instead of the original local4, and then passing that same variable through to .writeBytes(). I've removed the null check, by passing ifeq rather than ifne to make it skip the null check. However, once I do that, it either just doesn't send the packet, or I'm getting a stack depth inbalance.
All of these functions are in the same class, thus, in theory, should be able to call any of the methods I like from any of the other methods, without getting any reference errors.
This is what I've found so far:
JavaScript:
public class IncomingMessages
{
private var _SafeStr_14849:_SafeStr_1526; // Client or connection?
private var _SafeStr_14855:_SafeStr_261;
private var _SafeStr_20035:_SafeStr_2336;
private var _SafeStr_15408:String;
private var _SafeStr_20036:Boolean;
private var _SafeStr_19965:Boolean;
private var _SafeStr_16574:Vector.<_SafeStr_2275> = new Vector.<_SafeStr_2275>(0);
private var _SafeStr_20037:_SafeStr_4288;
public function IncomingMessages(k:_SafeStr_1526, _arg_2:_SafeStr_261)
{
this._SafeStr_14849 = k;
this._SafeStr_14855 = _arg_2;
var _local_3:_SafeStr_2282 = this._SafeStr_14855.connection;
if (_local_3 == null)
{
throw (new Error("Connection is required to initialize!"));
};
_local_3.addEventListener(Event.CONNECT, this._SafeStr_20023);
_local_3.addEventListener(Event.CLOSE, this._SafeStr_20024);
this.addHabboConnectionMessageEvent(new _SafeStr_3630(this._SafeStr_20025));//3542 IdentityAccountsEvent
this.addHabboConnectionMessageEvent(new _SafeStr_3469(this._SafeStr_16556));//1874 LoginFailedHotelClosedMessageEvent
this.addHabboConnectionMessageEvent(new _SafeStr_3811(this._SafeStr_20026));//1312 UniqueMachineIDEvent
this.addHabboConnectionMessageEvent(new _SafeStr_3777(this._SafeStr_20027));//2942 MaintenanceStatusMessageEvent
this.addHabboConnectionMessageEvent(new _SafeStr_2812(this._SafeStr_20028));//1922 AuthenticationOKMessageEvent
this.addHabboConnectionMessageEvent(new _SafeStr_3428(this._SafeStr_20029));//1552 CompleteDiffieHandshakeEvent
this.addHabboConnectionMessageEvent(new _SafeStr_3291(this._SafeStr_20030));//3943 InitDiffieHandshakeEvent
this.addHabboConnectionMessageEvent(new _SafeStr_3458(this._SafeStr_20031));//2788 GenericErrorEvent
this.addHabboConnectionMessageEvent(new DisconnectReasonEvent(this._SafeStr_20032));// DisconnectReasonEvent
this.addHabboConnectionMessageEvent(new _SafeStr_3374(this._SafeStr_20033));//1221 ErrorReportEvent
this.addHabboConnectionMessageEvent(new _SafeStr_3828(this._SafeStr_20034));//2285 PingMessageEvent
this._SafeStr_14849.context.events.addEventListener(Event.UNLOAD, this.unloading);
}
This is obviously listening for those incoming messages, and then calling the method that the event is linked too. From this, I know that, the function which should kick off the SSOTicket is in the function _SafeStr_20029().
This is function _SafeStr_20029()
JavaScript:
private function _SafeStr_20029(k:_SafeStr_2275):void
{
var _local_9:get;
var _local_2:_SafeStr_2282 = k.connection; // Gets the socket/connection?
var _local_3:_SafeStr_3428 = (k as _SafeStr_3428); // Casts the event to an incoming message to read the contents?
var _local_4:ByteArray = new ByteArray();
var _local_5:ByteArray = new ByteArray();
_local_4.writeBytes(CryptoTools._SafeStr_9526(_local_3._SafeStr_9531)); // String from CompleteDiffie
this._SafeStr_20037.verify(_local_4, _local_5, _local_4.length);
this._SafeStr_20037.dispose();
this._SafeStr_20035._SafeStr_15416(_local_5.toString(), 10);
var _local_6:String = this._SafeStr_20035._SafeStr_7527(16).toUpperCase();
if (!this._SafeStr_20035._SafeStr_15418())
{
return;
};
var _local_7:ByteArray = CryptoTools._SafeStr_9526(_local_6);
_local_7.position = 0;
var _local_8:get = this._SafeStr_14855._SafeStr_20041();
_local_8.init(_local_7);
if (_local_3._SafeStr_9532) // Checks the boolean from CompleteDiffie.
{
_local_9 = this._SafeStr_14855._SafeStr_20041();
_local_9.init(_local_7);
};
_local_2._SafeStr_16234(_local_8, _local_9); // Most likely sets the keys on the client/socket object to be used to encrypt/decrypt.
this._SafeStr_20036 = false; // Sets handshake failed to false?
this._SafeStr_14849._SafeStr_6469(_SafeStr_2270._SafeStr_6516); // This calls the event HANDSHAKE_OK.
this._SafeStr_14849._SafeStr_9533(_local_2); // This function sends the SSO.
}
From this, I know I can either send a packet which will fire the method _SafeStr_20029() or copy the last line which calls the SSO event and move it to another function.
I then went on and found where the ClientHello and InitCrypto are called from. They are called when a client connects (surprise, surprise)
Based on the first snippet, I know that function _SafeStr_20023() is called when the socket connects.
This is _SafeStr_20023()
JavaScript:
private function _SafeStr_20023(k:Event=null):void
{
var _local_2:_SafeStr_2282 = this._SafeStr_14855.connection;
if (_local_2 != null)
{
this._SafeStr_9540(); // Decoding a Base64 DH or RSA related and setting values, most likely n and e? - Removed.
this._SafeStr_14849._SafeStr_6469(_SafeStr_2270._SafeStr_6514); // Event tracking - HABBO_CONNECTION_EVENT_ESTABLISHED
this._SafeStr_19965 = false;
this._SafeStr_20036 = true;
this._SafeStr_14849._SafeStr_6469(_SafeStr_2270._SafeStr_6515); // Event tracking - HABBO_CONNECTION_EVENT_HANDSHAKING - Removed.
_local_2._SafeStr_16233(new _SafeStr_3392()); // Sends ClientHelloMessageComposer
_local_2._SafeStr_16233(new _SafeStr_3623()); // Sends InitDiffieHandshakeMessageComposer. Change to send SSOTicket instead? - Removed.
};
}
I then went ahead in the byte code, and replaced the InitDiffieHandshakeMessage and put the this._SafeStr_14849._SafeStr_9533(_local_2); in it's place. I've confirmed this by assembling and checking the AS3 representation and the function is valid at least. The lines with - Removed, are lines I've removed and also left there both times to see if it makes a difference. I've also tried setting _SafeStr_19965 to true and _SafeStr_20036 to false and seems to make no difference.
Basically, I just can't work out why the SSOTicketMessage is never sent, and I'm pretty confident it has something to do with the .send() function which is what is really sending the SSO message as seen below:
JavaScript:
public function _SafeStr_9533(k:_SafeStr_2282):void
{
var _local_5:_SafeStr_2804;
k.send(new _SafeStr_2802(401, this._SafeStr_19969, this._SafeStr_19966));
var _local_2:String = CommunicationUtils._SafeStr_6164(CommunicationUtils._SafeStr_6487);
var _local_3:String = CommunicationUtils._SafeStr_6488();
var _local_4:Array = Capabilities.version.split(" ");
k.send(new _SafeStr_2803(_local_2, _local_3, _local_4.join("/")));
if (((this._SafeStr_19967) && (this._SafeStr_19967.length > 0)))
{
_local_5 = new _SafeStr_2804(this._SafeStr_19967);
k.send(_local_5);
};
}
k is the socket object, and it's calling the .send() function which is the same function calling the encrypt function. Where as the ClientHello and InitCrypto is being called by another function _SafeStr_16233(), which is doing almost the same thing, but instead it's not checking for null on the RC4 object or passing the message into any other methods, other than .writeBytes().
And lastly, this is the .send() function and the .16233() function which does the encoding and then sending the packet/message.
JavaScript:
public function send(k:_SafeStr_2273):Boolean
{
if (disposed)
{
return (false);
};
if (((this._SafeStr_16252) && (!(this._SafeStr_16253))))
{
if (this._SafeStr_16254 == null)
{
this._SafeStr_16254 = new Vector.<_SafeStr_2273>(0);
};
this._SafeStr_16254.push(k);
return (false);
};
var _local_2:int = this._SafeStr_16251._SafeStr_7077(k);
if (_local_2 < 0)
{
return (false);
};
var _local_3:Array = k._SafeStr_15106();
var _local_4:ByteArray = this._SafeStr_16248.encode(_local_2, _local_3);
if (this._SafeStr_7079)
{
this._SafeStr_7079._SafeStr_16241(String(_local_2));
};
if (this._SafeStr_16249 == null)
{
return (false);
};
if (this._SafeStr_16245.connected)
{
this._SafeStr_16249._SafeStr_7524(_local_4);
this._SafeStr_16245.writeBytes(_local_4); // This writes bytes to the socket.
this._SafeStr_16245.flush();
}
else
{
return (false);
};
return (true);
}
public function _SafeStr_16233(k:_SafeStr_2273):Boolean
{
if (disposed)
{
return (false);
};
var _local_2:int = this._SafeStr_16251._SafeStr_7077(k);
if (_local_2 < 0)
{
return (false);
};
var _local_3:Array = k._SafeStr_15106();
var _local_4:ByteArray = this._SafeStr_16248.encode(_local_2, _local_3);
var _local_5:String = getQualifiedClassName(k);
var _local_6:Class = (getDefinitionByName(_local_5) as Class);
if (!ClassUtils.implementsInterface(_local_6, _SafeStr_3047))
{
return (false);
};
if (this._SafeStr_7079)
{
this._SafeStr_7079._SafeStr_16241(String(_local_2));
};
if (this._SafeStr_16245.connected)
{
this._SafeStr_16245.writeBytes(_local_4);
this._SafeStr_16245.flush();
}
else
{
return (false);
};
return (true);
}
Hopefully, this makes sense, and someone can help. Otherwise, when I find out, I'll post my results!
The SWF is an original with only host protection patched. RC4 and RSA keys are as original and haven't been touched.
SWF Version: PRODUCTION-202206221407-523648813
The SWF link: https://mega.nz/file/mogCFYrQ#zyme8fCsZtVc8Yx8scxol9uCRSfOMKNZXdz0SB0bCQs